nfdump/bin/bookkeeper.c
2018-06-24 11:12:31 +02:00

621 lines
18 KiB
C

/*
* Copyright (c) 2016, Peter Haag
* Copyright (c) 2014, Peter Haag
* Copyright (c) 2009, Peter Haag
* Copyright (c) 2004-2008, SWITCH - Teleinformatikdienste fuer Lehre und Forschung
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of the author nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#ifndef HAVE_SEMUN
union semun {
int val; // value for SETVAL
struct semid_ds *buf; // buffer for IPC_STAT & IPC_SET
u_short *array; // array for GETALL & SETALL
};
#endif
#include "util.h"
#include "bookkeeper.h"
static bookkeeper_list_t *bookkeeper_list = NULL;
/* function prototypes */
static key_t hash(char *str, int flag);
static void sem_lock(int sem_set_id);
static void sem_unlock(int sem_set_id);
static inline bookkeeper_list_t *Get_bookkeeper_list_entry(bookkeeper_t *bookkeeper);
/* Create shared memory object and set its size */
/* hash: compute hash value of string */
#define MULTIPLIER 37
static key_t hash(char *str, int flag) {
uint32_t h;
unsigned char *p;
char cleanPath[MAXPATHLEN];
if ( realpath(str, cleanPath) == NULL ) {
return -1;
}
h = 0;
for (p = (unsigned char*)cleanPath; *p != '\0'; p++)
h = MULTIPLIER * h + *p;
if ( flag ) {
h = MULTIPLIER * h + 'R';
}
// LogError("Bookeeper hash for path: '%s' -> '%s': %u flag: %i", str, cleanPath, h, flag);
return (key_t)h; // or, h % ARRAY_SIZE;
} // End of hash
// locks the semaphore, for exclusive access to the bookkeeping record
static void sem_lock(int sem_set_id) {
struct sembuf sem_op;
/* wait on the semaphore, unless it's value is non-negative. */
sem_op.sem_num = 0;
sem_op.sem_op = -1;
sem_op.sem_flg = 0;
if ( semop(sem_set_id, &sem_op, 1) == 0 )
return;
LogError("semop() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
} // End of sem_lock
// sem_unlock. un-locks the semaphore.
static void sem_unlock(int sem_set_id) {
struct sembuf sem_op;
/* signal the semaphore - increase its value by one. */
sem_op.sem_num = 0;
sem_op.sem_op = 1;
sem_op.sem_flg = 0;
if ( semop(sem_set_id, &sem_op, 1) == 0 )
return;
LogError("semop() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
} // End of sem_unlock
static inline bookkeeper_list_t *Get_bookkeeper_list_entry(bookkeeper_t *bookkeeper) {
bookkeeper_list_t *bookkeeper_list_entry;
if ( bookkeeper == NULL )
return NULL;
bookkeeper_list_entry = bookkeeper_list;
while ( bookkeeper_list_entry != NULL && bookkeeper_list_entry->bookkeeper != bookkeeper )
bookkeeper_list_entry = bookkeeper_list_entry->next;
return bookkeeper_list_entry;
} // End of Get_bookkeeper_list_entry
int InitBookkeeper(bookkeeper_t **bookkeeper, char *path, pid_t nfcapd_pid, pid_t launcher_pid) {
int sem_key, shm_key, shm_id, sem_id;
union semun sem_val;
bookkeeper_list_t **bookkeeper_list_entry;
*bookkeeper = NULL;
shm_key = hash(path, 0);
if ( shm_key == - 1 )
return ERR_PATHACCESS;
// check if the shared memory is already allocated
shm_id = shmget(shm_key, sizeof(bookkeeper_t), 0600);
if ( shm_id >= 0 ) {
// the segment already exists. Either a running process is active
// or an unclean shutdown happened
// map the segement and check the record
*bookkeeper = (bookkeeper_t *)shmat(shm_id, NULL, 0);
if ( *bookkeeper == (bookkeeper_t *)-1 ) {
LogError("shmat() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
return ERR_FAILED;
}
if ( (*bookkeeper)->nfcapd_pid <= 0 ) {
// rubbish or invalid pid of nfcapd process.
// Assume unclean shutdown or something else. We clean up and take this record.
memset((void *)(*bookkeeper), 0, sizeof(bookkeeper_t));
} else {
// check if the process created this record is still alive
int ret = kill((*bookkeeper)->nfcapd_pid, 0);
if ( ret == - 1 ) {
switch (errno) {
case ESRCH:
// process does not exist, we can clean up this record and use it
memset((void *)(*bookkeeper), 0, sizeof(bookkeeper_t));
break;
case EPERM:
// A process exists, but we are not allowed to signal this process
LogError("Another collector with pid %i but different user ID is already running, and configured for '%s'",
(*bookkeeper)->nfcapd_pid, path);
return ERR_EXISTS;
break;
default:
// This should never happen, but catch it anyway
LogError("semop() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
return ERR_FAILED;
}
} else {
// process exists;
LogError("Another collector with pid %i is already running, and configured for '%s'",
(*bookkeeper)->nfcapd_pid, path);
return ERR_EXISTS;
}
// if we pass this point, we have recycled an existing record
}
} else {
// no valid shared segment was found
switch (errno) {
case ENOENT:
// this is ok - no shared segemtn exists, we can create a new one below
break;
case EACCES:
// there is such a segment, but we are not allowed to get it
// Assume it's another nfcapd
LogError("Access denied to collector bookkeeping record.");
return ERR_EXISTS;
break;
default:
// This should never happen, but catch it anyway
LogError("semop() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
return ERR_FAILED;
}
// we now create a new segement, this hould not fail now
shm_id = shmget(shm_key, sizeof(bookkeeper_t), IPC_CREAT | 0600);
if ( shm_id == - 1 ) {
// but did anyway - give up
LogError("shmget() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
return ERR_FAILED;
}
*bookkeeper = (bookkeeper_t *)shmat(shm_id, NULL, 0);
if ( (*bookkeeper) == (bookkeeper_t *)-1 ) {
LogError("shmget() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
return ERR_FAILED;
}
memset((void *)(*bookkeeper), 0, sizeof(bookkeeper_t));
}
// at this point we now have a valid record and can proceed
(*bookkeeper)->nfcapd_pid = nfcapd_pid;
(*bookkeeper)->launcher_pid = launcher_pid;
(*bookkeeper)->sequence++;
// create semaphore
sem_key = hash(path, 1);
// this should never fail, as we aleady got a key for the shared memory
if ( sem_key == - 1 ) {
// .. but catch it anyway .. and release shared memory. something is fishy
struct shmid_ds buf;
shmdt((void *)(*bookkeeper));
shmctl(shm_id, IPC_RMID, &buf);
return ERR_FAILED;
}
// get the semaphore
sem_id = semget(sem_key, 1, IPC_CREAT | 0600);
if ( sem_id == - 1 ) {
struct shmid_ds buf;
// this should not have failed
LogError("semget() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
// release shared memory
shmdt((void *)(*bookkeeper));
shmctl(shm_id, IPC_RMID, &buf);
return ERR_FAILED;
}
// initialize the semaphore
sem_val.val = 1;
if ( semctl(sem_id, 0, SETVAL, sem_val) == -1) {
struct shmid_ds buf;
// this should not have failed
LogError("semctl() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
// release shared memory
shmdt((void *)(*bookkeeper));
shmctl(shm_id, IPC_RMID, &buf);
return ERR_FAILED;
}
bookkeeper_list_entry = &bookkeeper_list;
while ( *bookkeeper_list_entry != NULL )
bookkeeper_list_entry = &((*bookkeeper_list_entry)->next);
(*bookkeeper_list_entry) = (bookkeeper_list_t *)malloc(sizeof(bookkeeper_list_t));
if ( !*bookkeeper_list_entry ) {
struct shmid_ds buf;
LogError("malloc() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
shmdt((void *)(*bookkeeper));
shmctl(shm_id, IPC_RMID, &buf);
semctl( sem_id, 0, IPC_RMID );
return ERR_FAILED;
}
memset((void *)*bookkeeper_list_entry, 0, sizeof(bookkeeper_list_t));
(*bookkeeper_list_entry)->shm_id = shm_id;
(*bookkeeper_list_entry)->sem_id = sem_id;
(*bookkeeper_list_entry)->bookkeeper = *bookkeeper;
(*bookkeeper_list_entry)->next = NULL;
// we are done
return BOOKKEEPER_OK;
} // End of InitBookkeeper
int AccessBookkeeper(bookkeeper_t **bookkeeper, char *path) {
bookkeeper_list_t **bookkeeper_list_entry;
int sem_key, shm_key, shm_id, sem_id;
*bookkeeper = NULL;
shm_key = hash(path, 0);
if ( shm_key == - 1 )
return ERR_PATHACCESS;
// check if the shared memory is already allocated
shm_id = shmget(shm_key, sizeof(bookkeeper_t), 0600);
if ( shm_id < 0 ) {
// the segment does not exists. Check why
switch (errno) {
case ENOENT:
// no shared segemtn exists.
return ERR_NOTEXISTS;
break;
case EACCES:
// there is such a segment, but we are not allowed to get it
// Assume it's another nfcapd
LogError("Access denied to collector bookkeeping record.");
return ERR_FAILED;
break;
default:
// This should never happen, but catch it anyway
LogError("semop() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
return ERR_FAILED;
}
// not reached
}
// at this point we now have a valid record and can proceed
// create semaphore
sem_key = hash(path, 1);
// this should never fail, as we aleady got a key for the shared memory
if ( sem_key == - 1 ) {
// .. but catch it anyway .. and release shared memory. something is fishy
return ERR_FAILED;
}
// get the semaphore
sem_id = semget(sem_key, 1, 0600);
if ( sem_id == - 1 ) {
// this should not have failed
LogError("semget() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
return ERR_FAILED;
}
// map the shared segment
*bookkeeper = (bookkeeper_t *)shmat(shm_id, NULL, 0);
if ( *bookkeeper == (bookkeeper_t *)-1 ) {
LogError("shmat() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
return ERR_FAILED;
}
bookkeeper_list_entry = &bookkeeper_list;
while ( *bookkeeper_list_entry != NULL && (*bookkeeper_list_entry)->bookkeeper != NULL )
bookkeeper_list_entry = &((*bookkeeper_list_entry)->next);
// allocate new slot, else use unused slot
if ( *bookkeeper_list_entry == NULL ) {
(*bookkeeper_list_entry) = (bookkeeper_list_t *)malloc(sizeof(bookkeeper_list_t));
if ( !*bookkeeper_list_entry ) {
LogError("malloc() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
return ERR_FAILED;
}
memset((void *)*bookkeeper_list_entry, 0, sizeof(bookkeeper_list_t));
}
(*bookkeeper_list_entry)->shm_id = shm_id;
(*bookkeeper_list_entry)->sem_id = sem_id;
(*bookkeeper_list_entry)->bookkeeper = *bookkeeper;
(*bookkeeper_list_entry)->next = NULL;
return BOOKKEEPER_OK;
} // End of AccessBookkeeper
void ReleaseBookkeeper(bookkeeper_t *bookkeeper, int destroy) {
bookkeeper_list_t *bookkeeper_list_entry;
struct shmid_ds buf;
if ( !bookkeeper )
return;
bookkeeper_list_entry = Get_bookkeeper_list_entry(bookkeeper);
if ( !bookkeeper_list_entry ) {
// this should never happen
LogError("Software error in %s line %d: %s", __FILE__, __LINE__, "Entry not found in list");
return;
}
// detach from my process addr space memory
if ( shmdt((void *)bookkeeper) == -1 ) {
// ups ..
LogError("shmdt() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
}
bookkeeper = NULL;
if ( destroy == 0 ) {
// Entry no longer valid
bookkeeper_list_entry->bookkeeper = NULL;
bookkeeper_list_entry->shm_id = 0;
bookkeeper_list_entry->sem_id = 0;
return;
}
// prevent other proceeses to access the share memory, while we are removing it
// try to clean up.
sem_lock(bookkeeper_list_entry->sem_id);
if ( shmctl(bookkeeper_list_entry->shm_id, IPC_RMID, &buf) ) {
// ups ..
LogError("shmctl() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
}
sem_unlock(bookkeeper_list_entry->sem_id);
if ( semctl( bookkeeper_list_entry->sem_id, 0, IPC_RMID ) == -1 ) {
// ups ..
LogError("semctl() error in %s line %d: %s", __FILE__, __LINE__, strerror(errno) );
}
// Entry no longer valid
bookkeeper_list_entry->bookkeeper = NULL;
bookkeeper_list_entry->shm_id = 0;
bookkeeper_list_entry->sem_id = 0;
} // End of ReleaseBookkeeper
int LookBooks(bookkeeper_t *bookkeeper) {
bookkeeper_list_t *bookkeeper_list_entry;
if ( !bookkeeper )
return 0;
bookkeeper_list_entry = Get_bookkeeper_list_entry(bookkeeper);
if ( !bookkeeper_list_entry ) {
// this should never happen
LogError("Software error in %s line %d: %s", __FILE__, __LINE__, "Entry not found in list");
return 1;
}
sem_lock(bookkeeper_list_entry->sem_id);
return 0;
} // End of LookBooks
int UnlookBooks(bookkeeper_t *bookkeeper) {
bookkeeper_list_t *bookkeeper_list_entry;
if ( !bookkeeper )
return 0;
bookkeeper_list_entry = Get_bookkeeper_list_entry(bookkeeper);
if ( !bookkeeper_list_entry ) {
// this should never happen
LogError("Software error in %s line %d: %s", __FILE__, __LINE__, "Entry not found in list");
return 1;
}
sem_unlock(bookkeeper_list_entry->sem_id);
return 0;
} // End of UnlookBooks
void ClearBooks(bookkeeper_t *bookkeeper, bookkeeper_t *tmp_books) {
bookkeeper_list_t *bookkeeper_list_entry;
if ( !bookkeeper )
return;
bookkeeper_list_entry = Get_bookkeeper_list_entry(bookkeeper);
if ( !bookkeeper_list_entry ) {
// this should never happen
LogError("Software error in %s line %d: %s", __FILE__, __LINE__, "Entry not found in list");
return;
}
sem_lock(bookkeeper_list_entry->sem_id);
// backup copy
if ( tmp_books != NULL ) {
memcpy((void *)tmp_books, (void *)bookkeeper, sizeof(bookkeeper_t));
}
bookkeeper->first = 0;
bookkeeper->last = 0;
bookkeeper->numfiles = 0;
bookkeeper->filesize = 0;
bookkeeper->sequence++;
sem_unlock(bookkeeper_list_entry->sem_id);
} // End of ClearBooks
uint64_t BookSequence(bookkeeper_t *bookkeeper) {
bookkeeper_list_t *bookkeeper_list_entry;
uint64_t seq;
if ( !bookkeeper )
return 0;
bookkeeper_list_entry = Get_bookkeeper_list_entry(bookkeeper);
if ( !bookkeeper_list_entry ) {
// this should never happen
LogError("Software error in %s line %d: %s", __FILE__, __LINE__, "Entry not found in list");
return 0;
}
sem_lock(bookkeeper_list_entry->sem_id);
seq = bookkeeper->sequence;
sem_unlock(bookkeeper_list_entry->sem_id);
return seq;
} // End of BookSequence
void UpdateBooks(bookkeeper_t *bookkeeper, time_t when, uint64_t size) {
bookkeeper_list_t *bookkeeper_list_entry;
if ( !bookkeeper )
return;
bookkeeper_list_entry = Get_bookkeeper_list_entry(bookkeeper);
if ( !bookkeeper_list_entry ) {
// this should never happen
LogError("Software error in %s line %d: %s", __FILE__, __LINE__, "Entry not found in list");
return;
}
sem_lock(bookkeeper_list_entry->sem_id);
if ( bookkeeper->first == 0 )
bookkeeper->first = when;
bookkeeper->last = when;
bookkeeper->numfiles++;
bookkeeper->filesize += size;
bookkeeper->sequence++;
sem_unlock(bookkeeper_list_entry->sem_id);
} // End of UpdateBooks
void UpdateBooksParam(bookkeeper_t *bookkeeper, time_t lifetime, uint64_t maxsize) {
bookkeeper_list_t *bookkeeper_list_entry;
if ( !bookkeeper )
return;
bookkeeper_list_entry = Get_bookkeeper_list_entry(bookkeeper);
if ( !bookkeeper_list_entry ) {
// this should never happen
LogError("Software error in %s line %d: %s", __FILE__, __LINE__, "Entry not found in list");
return;
}
sem_lock(bookkeeper_list_entry->sem_id);
bookkeeper->max_lifetime = lifetime;
bookkeeper->max_filesize = maxsize;
bookkeeper->sequence++;
sem_unlock(bookkeeper_list_entry->sem_id);
} // End of UpdateBooksParam
void PrintBooks(bookkeeper_t *bookkeeper) {
bookkeeper_list_t *bookkeeper_list_entry;
struct tm *ts;
time_t t;
char string[32];
if ( !bookkeeper ) {
printf("No bookkeeper record available!\n");
return;
}
bookkeeper_list_entry = Get_bookkeeper_list_entry(bookkeeper);
if ( !bookkeeper_list_entry ) {
// this should never happen
LogError("Software error in %s line %d: %s", __FILE__, __LINE__, "Entry not found in list");
return;
}
sem_lock(bookkeeper_list_entry->sem_id);
printf("Collector process: %lu\n", (unsigned long)bookkeeper->nfcapd_pid);
if ( bookkeeper->launcher_pid )
printf("Launcher process: %lu\n", (unsigned long)bookkeeper->launcher_pid);
else
printf("Launcher process: <none>\n");
printf("Record sequence : %llu\n", (unsigned long long)bookkeeper->sequence);
t = bookkeeper->first;
ts = localtime(&t);
strftime(string, 31, "%Y-%m-%d %H:%M:%S", ts);
string[31] = '\0';
printf("First : %s\n", bookkeeper->first ? string : "<not set>");
t = bookkeeper->last;
ts = localtime(&t);
strftime(string, 31, "%Y-%m-%d %H:%M:%S", ts);
string[31] = '\0';
printf("Last : %s\n", bookkeeper->last ? string : "<not set>");
printf("Number of files : %llu\n", (unsigned long long)bookkeeper->numfiles);
printf("Total file size : %llu\n", (unsigned long long)bookkeeper->filesize);
printf("Max file size : %llu\n", (unsigned long long)bookkeeper->max_filesize);
printf("Max life time : %llu\n", (unsigned long long)bookkeeper->max_lifetime);
sem_unlock(bookkeeper_list_entry->sem_id);
} // End of PrintBooks