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); }
static uint32_t pairlist_hash(void const *data) { return fr_hash_string(((PAIR_LIST const *)data)->name); }
static uint32_t pairlist_hash(const void *data) { return fr_hash_string(((const PAIR_LIST *)data)->name); }
/** 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; }
/** 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; }