예제 #1
0
static int virtual_server_idx(char const *name)
{
	uint32_t hash;

	if (!name) return 0;

	hash = fr_hash_string(name);

	return hash & (VIRTUAL_SERVER_HASH_SIZE - 1);
}
예제 #2
0
static uint32_t pairlist_hash(void const *data)
{
	return fr_hash_string(((PAIR_LIST const *)data)->name);
}
예제 #3
0
static uint32_t pairlist_hash(const void *data)
{
	return fr_hash_string(((const PAIR_LIST *)data)->name);
}
예제 #4
0
/** Open a new log file, or maybe an existing one.
 *
 * When multithreaded, the FD is locked via a mutex.  This way we're
 * sure that no other thread is writing to the file.
 *
 * @param lf The logfile context returned from fr_logfile_init().
 * @param filename the file to open.
 * @param permissions to use.
 * @return an FD used to write to the file, or -1 on error.
 */
int fr_logfile_open(fr_logfile_t *lf, char const *filename, mode_t permissions)
{
	int i;
	uint32_t hash;
	time_t now = time(NULL);
	struct stat st;

	if (!lf || !filename) return -1;

	hash = fr_hash_string(filename);

	PTHREAD_MUTEX_LOCK(&lf->mutex);

	/*
	 *	Clean up old entries.
	 */
	for (i = 0; i < lf->max_entries; i++) {
		if (!lf->entries[i].filename) continue;

		/*
		 *	FIXME: make this configurable?
		 */
		if ((lf->entries[i].last_used + 30) < now) {
			/*
			 *	This will block forever if a thread is
			 *	doing something stupid.
			 */
			TALLOC_FREE(lf->entries[i].filename);
			close(lf->entries[i].fd);
		}
	}

	/*
	 *	Find the matching entry.
	 */
	for (i = 0; i < lf->max_entries; i++) {
		if (!lf->entries[i].filename) continue;

		if (lf->entries[i].hash == hash) {
			/*
			 *	Same hash but different filename.  Give up.
			 */
			if (strcmp(lf->entries[i].filename, filename) != 0) {
				PTHREAD_MUTEX_UNLOCK(&lf->mutex);
				return -1;
			}
			/*
			 *	Someone else failed to create the entry.
			 */
			if (!lf->entries[i].filename) {
				PTHREAD_MUTEX_UNLOCK(&lf->mutex);
				return -1;
			}
			goto do_return;
		}
	}

	/*
	 *	Find an unused entry
	 */
	for (i = 0; i < lf->max_entries; i++) {
		if (!lf->entries[i].filename) break;
	}

	if (i >= lf->max_entries) {
		fr_strerror_printf("Too many different filenames");
		PTHREAD_MUTEX_UNLOCK(&(lf->mutex));
		return -1;
	}

	/*
	 *	Create a new entry.
	 */

	lf->entries[i].hash = hash;
	lf->entries[i].filename = talloc_strdup(lf->entries, filename);
	lf->entries[i].fd = -1;

	lf->entries[i].fd = open(filename, O_WRONLY | O_APPEND | O_CREAT, permissions);
	if (lf->entries[i].fd < 0) {
		mode_t dirperm;
		char *p, *dir;

		/*
		 *	Maybe the directory doesn't exist.  Try to
		 *	create it.
		 */
		dir = talloc_strdup(lf, filename);
		if (!dir) goto error;
		p = strrchr(dir, FR_DIR_SEP);
		if (!p) goto error;
		*p = '\0';

		/*
		 *	Ensure that the 'x' bit is set, so that we can
		 *	read the directory.
		 */
		dirperm = permissions;
		if ((dirperm & 0600) != 0) dirperm |= 0100;
		if ((dirperm & 0060) != 0) dirperm |= 0010;
		if ((dirperm & 0006) != 0) dirperm |= 0001;

		if (rad_mkdir(dir, dirperm) < 0) {
			talloc_free(dir);
			goto error;
		}
		talloc_free(dir);

		lf->entries[i].fd = open(filename, O_WRONLY | O_CREAT, permissions);
		if (lf->entries[i].fd < 0) {
			fr_strerror_printf("Failed to open file %s: %s",
					   filename, strerror(errno));
			goto error;
		} /* else fall through to creating the rest of the entry */
	} /* else the file was already opened */

do_return:
	/*
	 *	Lock from the start of the file.
	 */
	if (lseek(lf->entries[i].fd, 0, SEEK_SET) < 0) {
		fr_strerror_printf("Failed to seek in file %s: %s",
				   filename, strerror(errno));

	error:
		lf->entries[i].hash = 0;
		TALLOC_FREE(lf->entries[i].filename);
		close(lf->entries[i].fd);
		lf->entries[i].fd = -1;

		PTHREAD_MUTEX_UNLOCK(&(lf->mutex));
		return -1;
	}

	if (rad_lockfd(lf->entries[i].fd, 0) < 0) {
		fr_strerror_printf("Failed to lock file %s: %s",
				   filename, strerror(errno));
		goto error;
	}

	/*
	 *	Maybe someone deleted the file while we were waiting
	 *	for the lock.  If so, re-open it.
	 */
	if (fstat(lf->entries[i].fd, &st) < 0) {
		fr_strerror_printf("Failed to stat file %s: %s",
				   filename, strerror(errno));
		goto error;
	}

	if (st.st_nlink == 0) {
		close(lf->entries[i].fd);
		lf->entries[i].fd = open(filename, O_WRONLY | O_CREAT, permissions);
		if (lf->entries[i].fd < 0) {
			fr_strerror_printf("Failed to open file %s: %s",
					   filename, strerror(errno));
			goto error;
		}
	}

	/*
	 *	Seek to the end of the file before returning the FD to
	 *	the caller.
	 */
	lseek(lf->entries[i].fd, 0, SEEK_END);

	/*
	 *	Return holding the mutex for the entry.
	 */
	lf->entries[i].last_used = now;
	lf->entries[i].dup = dup(lf->entries[i].fd);
	if (lf->entries[i].dup < 0) goto error;

	return lf->entries[i].dup;
}
예제 #5
0
/** Open a new log file, or maybe an existing one.
 *
 * When multithreaded, the FD is locked via a mutex.  This way we're
 * sure that no other thread is writing to the file.
 *
 * @param ef The logfile context returned from exfile_init().
 * @param filename the file to open.
 * @param permissions to use.
 * @param append If true seek to the end of the file.
 * @return an FD used to write to the file, or -1 on error.
 */
int exfile_open(exfile_t *ef, char const *filename, mode_t permissions, bool append)
{
	uint32_t i, tries;
	uint32_t hash;
	time_t now = time(NULL);
	struct stat st;

	if (!ef || !filename) return -1;

	hash = fr_hash_string(filename);

	PTHREAD_MUTEX_LOCK(&ef->mutex);

	/*
	 *	Clean up old entries.
	 */
	for (i = 0; i < ef->max_entries; i++) {
		if (!ef->entries[i].filename) continue;
		if ((ef->entries[i].last_used + ef->max_idle) < now) {
			/*
			 *	This will block forever if a thread is
			 *	doing something stupid.
			 */
			TALLOC_FREE(ef->entries[i].filename);
			close(ef->entries[i].fd);
		}
	}

	/*
	 *	Find the matching entry.
	 */
	for (i = 0; i < ef->max_entries; i++) {
		if (!ef->entries[i].filename) continue;

		if (ef->entries[i].hash == hash) {
			/*
			 *	Same hash but different filename.  Give up.
			 */
			if (strcmp(ef->entries[i].filename, filename) != 0) {
				PTHREAD_MUTEX_UNLOCK(&ef->mutex);
				return -1;
			}
			/*
			 *	Someone else failed to create the entry.
			 */
			if (!ef->entries[i].filename) {
				PTHREAD_MUTEX_UNLOCK(&ef->mutex);
				return -1;
			}
			goto do_return;
		}
	}

	/*
	 *	Find an unused entry
	 */
	for (i = 0; i < ef->max_entries; i++) {
		if (!ef->entries[i].filename) break;
	}

	if (i >= ef->max_entries) {
		fr_strerror_printf("Too many different filenames");
		PTHREAD_MUTEX_UNLOCK(&(ef->mutex));
		return -1;
	}

	/*
	 *	Create a new entry.
	 */

	ef->entries[i].hash = hash;
	ef->entries[i].filename = talloc_strdup(ef->entries, filename);
	ef->entries[i].fd = -1;

	ef->entries[i].fd = open(filename, O_RDWR | O_APPEND | O_CREAT, permissions);
	if (ef->entries[i].fd < 0) {
		mode_t dirperm;
		char *p, *dir;

		/*
		 *	Maybe the directory doesn't exist.  Try to
		 *	create it.
		 */
		dir = talloc_strdup(ef, filename);
		if (!dir) goto error;
		p = strrchr(dir, FR_DIR_SEP);
		if (!p) {
			fr_strerror_printf("No '/' in '%s'", filename);
			goto error;
		}
		*p = '\0';

		/*
		 *	Ensure that the 'x' bit is set, so that we can
		 *	read the directory.
		 */
		dirperm = permissions;
		if ((dirperm & 0600) != 0) dirperm |= 0100;
		if ((dirperm & 0060) != 0) dirperm |= 0010;
		if ((dirperm & 0006) != 0) dirperm |= 0001;

		if (rad_mkdir(dir, dirperm, -1, -1) < 0) {
			fr_strerror_printf("Failed to create directory %s: %s",
					   dir, strerror(errno));
			talloc_free(dir);
			goto error;
		}
		talloc_free(dir);

		ef->entries[i].fd = open(filename, O_WRONLY | O_CREAT, permissions);
		if (ef->entries[i].fd < 0) {
			fr_strerror_printf("Failed to open file %s: %s",
					   filename, strerror(errno));
			goto error;
		} /* else fall through to creating the rest of the entry */
	} /* else the file was already opened */

do_return:
	/*
	 *	Lock from the start of the file.
	 */
	if (lseek(ef->entries[i].fd, 0, SEEK_SET) < 0) {
		fr_strerror_printf("Failed to seek in file %s: %s", filename, strerror(errno));

	error:
		ef->entries[i].hash = 0;
		TALLOC_FREE(ef->entries[i].filename);
		close(ef->entries[i].fd);
		ef->entries[i].fd = -1;

		PTHREAD_MUTEX_UNLOCK(&(ef->mutex));
		return -1;
	}

	/*
	 *	Try to lock it.  If we can't lock it, it's because
	 *	some reader has re-named the file to "foo.work" and
	 *	locked it.  So, we close the current file, re-open it,
	 *	and try again/
	 */
	tries = 0;
	while ((rad_lockfd_nonblock(ef->entries[i].fd, 0) < 0) &&
	       (tries < 4)) {
		if (errno != EAGAIN) {
			fr_strerror_printf("Failed to lock file %s: %s", filename, strerror(errno));
			goto error;
		}

		close(ef->entries[i].fd);
		ef->entries[i].fd = open(filename, O_WRONLY | O_CREAT, permissions);
		if (ef->entries[i].fd < 0) {
			fr_strerror_printf("Failed to open file %s: %s",
					   filename, strerror(errno));
			goto error;
		}
	}

	if (tries >= 4) {
		fr_strerror_printf("Failed to lock file %s: too many tries", filename);
		goto error;
	}

	/*
	 *	Maybe someone deleted the file while we were waiting
	 *	for the lock.  If so, re-open it.
	 */
	if (fstat(ef->entries[i].fd, &st) < 0) {
		fr_strerror_printf("Failed to stat file %s: %s", filename, strerror(errno));
		goto error;
	}

	if (st.st_nlink == 0) {
		close(ef->entries[i].fd);
		ef->entries[i].fd = open(filename, O_WRONLY | O_CREAT, permissions);
		if (ef->entries[i].fd < 0) {
			fr_strerror_printf("Failed to open file %s: %s",
					   filename, strerror(errno));
			goto error;
		}
	}

	/*
	 *	Seek to the end of the file before returning the FD to
	 *	the caller.
	 */
	if (append) lseek(ef->entries[i].fd, 0, SEEK_END);

	/*
	 *	Return holding the mutex for the entry.
	 */
	ef->entries[i].last_used = now;
	ef->entries[i].dup = dup(ef->entries[i].fd);
	if (ef->entries[i].dup < 0) {
		fr_strerror_printf("Failed calling dup(): %s", strerror(errno));
		goto error;
	}

	return ef->entries[i].dup;
}