mirror of https://github.com/sipwise/rtpengine.git
Change-Id: I8102144ab1508fe815be84d727f6fa3234fd0994changes/03/9503/6
parent
e3a7248b01
commit
411c3b3373
@ -1 +1,2 @@
|
||||
daemon/rtpengine /usr/sbin/
|
||||
recording-daemon/rtpengine-recording /usr/sbin/
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
.depend
|
||||
*.o
|
||||
core
|
||||
core.*
|
||||
.ycm_extra_conf.pyc
|
||||
@ -0,0 +1,102 @@
|
||||
import os
|
||||
import ycm_core
|
||||
from clang_helpers import PrepareClangFlags
|
||||
|
||||
# Set this to the absolute path to the folder (NOT the file!) containing the
|
||||
# compile_commands.json file to use that instead of 'flags'. See here for
|
||||
# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
|
||||
# Most projects will NOT need to set this to anything; you can just change the
|
||||
# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
|
||||
compilation_database_folder = ''
|
||||
|
||||
# These are the compilation flags that will be used in case there's no
|
||||
# compilation database set.
|
||||
flags = [
|
||||
'-g',
|
||||
'-Wall',
|
||||
'-pthread',
|
||||
'-fno-strict-aliasing',
|
||||
'-I/usr/include/glib-2.0',
|
||||
'-I/usr/lib/x86_64-linux-gnu/glib-2.0/include',
|
||||
'-pthread',
|
||||
'-D_GNU_SOURCE',
|
||||
'-D__DEBUG=1',
|
||||
'-D__YCM=1',
|
||||
'-O2',
|
||||
'-fstack-protector',
|
||||
'--param=ssp-buffer-size=4',
|
||||
'-Wformat',
|
||||
'-Werror=format-security',
|
||||
'-D_FORTIFY_SOURCE=2',
|
||||
# THIS IS IMPORTANT! Without a "-std=<something>" flag, clang won't know which
|
||||
# language to use when compiling headers. So it will guess. Badly. So C++
|
||||
# headers will be compiled as C headers. You don't want that so ALWAYS specify
|
||||
# a "-std=<something>".
|
||||
# For a C project, you would set this to something like 'c99' instead of
|
||||
# 'c++11'.
|
||||
'-std=c99',
|
||||
# ...and the same thing goes for the magic -x option which specifies the
|
||||
# language that the files to be compiled are written in. This is mostly
|
||||
# relevant for c++ headers.
|
||||
# For a C project, you would set this to 'c' instead of 'c++'.
|
||||
'-x',
|
||||
'c',
|
||||
]
|
||||
|
||||
if compilation_database_folder:
|
||||
database = ycm_core.CompilationDatabase( compilation_database_folder )
|
||||
else:
|
||||
database = None
|
||||
|
||||
|
||||
def DirectoryOfThisScript():
|
||||
return os.path.dirname( os.path.abspath( __file__ ) )
|
||||
|
||||
|
||||
def MakeRelativePathsInFlagsAbsolute( flags, working_directory ):
|
||||
if not working_directory:
|
||||
return flags
|
||||
new_flags = []
|
||||
make_next_absolute = False
|
||||
path_flags = [ '-isystem', '-I', '-iquote', '--sysroot=' ]
|
||||
for flag in flags:
|
||||
new_flag = flag
|
||||
|
||||
if make_next_absolute:
|
||||
make_next_absolute = False
|
||||
if not flag.startswith( '/' ):
|
||||
new_flag = os.path.join( working_directory, flag )
|
||||
|
||||
for path_flag in path_flags:
|
||||
if flag == path_flag:
|
||||
make_next_absolute = True
|
||||
break
|
||||
|
||||
if flag.startswith( path_flag ):
|
||||
path = flag[ len( path_flag ): ]
|
||||
new_flag = path_flag + os.path.join( working_directory, path )
|
||||
break
|
||||
|
||||
if new_flag:
|
||||
new_flags.append( new_flag )
|
||||
return new_flags
|
||||
|
||||
|
||||
def FlagsForFile( filename ):
|
||||
if database:
|
||||
# Bear in mind that compilation_info.compiler_flags_ does NOT return a
|
||||
# python list, but a "list-like" StringVec object
|
||||
compilation_info = database.GetCompilationInfoForFile( filename )
|
||||
final_flags = PrepareClangFlags(
|
||||
MakeRelativePathsInFlagsAbsolute(
|
||||
compilation_info.compiler_flags_,
|
||||
compilation_info.compiler_working_dir_ ),
|
||||
filename )
|
||||
else:
|
||||
relative_to = DirectoryOfThisScript()
|
||||
final_flags = MakeRelativePathsInFlagsAbsolute( flags, relative_to )
|
||||
|
||||
return {
|
||||
'flags': final_flags,
|
||||
'do_cache': True
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
TARGET= rtpengine-recording
|
||||
|
||||
CC?=gcc
|
||||
CFLAGS= -g -Wall -pthread
|
||||
CFLAGS+= -std=c99
|
||||
CFLAGS+= -D_GNU_SOURCE -D_POSIX_SOURCE -D_POSIX_C_SOURCE
|
||||
CFLAGS+= `pkg-config --cflags glib-2.0`
|
||||
CFLAGS+= `pkg-config --cflags gthread-2.0`
|
||||
#CFLAGS+= `pcre-config --cflags`
|
||||
|
||||
ifeq ($(DBG),yes)
|
||||
CFLAGS+= -D__DEBUG=1
|
||||
else
|
||||
CFLAGS+= -O3
|
||||
endif
|
||||
|
||||
LDFLAGS= -lm
|
||||
LDFLAGS+= `pkg-config --libs glib-2.0`
|
||||
LDFLAGS+= `pkg-config --libs gthread-2.0`
|
||||
#LDFLAGS+= `pcre-config --libs`
|
||||
|
||||
ifneq ($(DBG),yes)
|
||||
DPKG_BLDFLGS= $(shell which dpkg-buildflags 2>/dev/null)
|
||||
ifneq ($(DPKG_BLDFLGS),)
|
||||
# support http://wiki.debian.org/Hardening for >=wheezy
|
||||
CFLAGS+= `dpkg-buildflags --get CFLAGS`
|
||||
CPPFLAGS+= `dpkg-buildflags --get CPPFLAGS`
|
||||
LDFLAGS+= `dpkg-buildflags --get LDFLAGS`
|
||||
endif
|
||||
endif
|
||||
|
||||
SRCS= epoll.c garbage.c inotify.c main.c metafile.c stream.c aux.c
|
||||
OBJS= $(SRCS:.c=.o)
|
||||
|
||||
|
||||
.PHONY: all dep clean tests debug
|
||||
|
||||
all:
|
||||
$(MAKE) $(TARGET)
|
||||
|
||||
debug:
|
||||
$(MAKE) DBG=yes all
|
||||
|
||||
dep: .depend
|
||||
|
||||
clean:
|
||||
rm -f $(OBJS) $(TARGET) .depend core core.*
|
||||
|
||||
.depend: $(SRCS) Makefile
|
||||
$(CC) $(CFLAGS) -M $(SRCS) | sed -e 's/:/ .depend:/' > .depend
|
||||
|
||||
$(TARGET): $(OBJS) .depend Makefile
|
||||
$(CC) $(CFLAGS) -o $@ $(OBJS) $(LDFLAGS)
|
||||
|
||||
$(OBJS): Makefile
|
||||
|
||||
|
||||
include .depend
|
||||
@ -0,0 +1,21 @@
|
||||
#include "aux.h"
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
|
||||
int __thread __sscanf_hack_var;
|
||||
|
||||
|
||||
int __sscanf_match(const char *str, const char *fmt, ...) {
|
||||
va_list ap;
|
||||
|
||||
__sscanf_hack_var = 0; // to make sure that sscanf consumes the entire string
|
||||
|
||||
va_start(ap, fmt);
|
||||
int ret = vsscanf(str, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (__sscanf_hack_var == 0)
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
#ifndef _AUX_H_
|
||||
#define _AUX_H_
|
||||
|
||||
extern int __thread __sscanf_hack_var;
|
||||
|
||||
#define sscanf_match(str, format, ...) __sscanf_match(str, format "%n", ##__VA_ARGS__, &__sscanf_hack_var)
|
||||
int __sscanf_match(const char *str, const char *fmt, ...) __attribute__ ((__format__ (__scanf__, 2, 3)));
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,64 @@
|
||||
#include "epoll.h"
|
||||
#include <sys/epoll.h>
|
||||
#include <glib.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include "log.h"
|
||||
#include "main.h"
|
||||
#include "garbage.h"
|
||||
|
||||
|
||||
static int epoll_fd = -1;
|
||||
|
||||
|
||||
void epoll_setup(void) {
|
||||
epoll_fd = epoll_create1(0);
|
||||
if (epoll_fd == -1)
|
||||
die_errno("epoll_create1 failed");
|
||||
|
||||
}
|
||||
|
||||
|
||||
int epoll_add(int fd, uint32_t events, handler_t *handler) {
|
||||
struct epoll_event epev = { .events = events | EPOLLET, .data = { .ptr = handler } };
|
||||
int ret = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &epev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void epoll_del(int fd) {
|
||||
epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
|
||||
}
|
||||
|
||||
|
||||
void *poller_thread(void *ptr) {
|
||||
struct epoll_event epev;
|
||||
unsigned int me_num = GPOINTER_TO_UINT(ptr);
|
||||
|
||||
dbg("poller thread %u running", me_num);
|
||||
|
||||
while (!shutdown_flag) {
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
|
||||
int ret = epoll_wait(epoll_fd, &epev, 1, 10000);
|
||||
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||
|
||||
if (ret == -1)
|
||||
die_errno("epoll_wait failed");
|
||||
|
||||
if (ret > 0) {
|
||||
dbg("thread %u handling event", me_num);
|
||||
|
||||
handler_t *handler = epev.data.ptr;
|
||||
handler->func(handler);
|
||||
}
|
||||
|
||||
garbage_collect(me_num);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
void epoll_cleanup(void) {
|
||||
close(epoll_fd);
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
#ifndef _EPOLL_H_
|
||||
#define _EPOLL_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <stdint.h>
|
||||
#include "types.h"
|
||||
|
||||
|
||||
void epoll_setup(void);
|
||||
void epoll_cleanup(void);
|
||||
|
||||
int epoll_add(int fd, uint32_t events, handler_t *handler);
|
||||
void epoll_del(int fd);
|
||||
|
||||
|
||||
void *poller_thread(void *ptr);
|
||||
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,96 @@
|
||||
#include "garbage.h"
|
||||
#include <glib.h>
|
||||
#include <pthread.h>
|
||||
#include "log.h"
|
||||
|
||||
|
||||
typedef struct {
|
||||
void *ptr;
|
||||
void (*free_func)(void *);
|
||||
int *wait_threads;
|
||||
unsigned int array_len;
|
||||
unsigned int threads_left;
|
||||
} garbage_t;
|
||||
|
||||
|
||||
static pthread_mutex_t garbage_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static GQueue garbage = G_QUEUE_INIT;
|
||||
static volatile int garbage_thread_num;
|
||||
|
||||
|
||||
unsigned int garbage_new_thread_num(void) {
|
||||
return g_atomic_int_add(&garbage_thread_num, 1);
|
||||
}
|
||||
|
||||
|
||||
void garbage_add(void *ptr, free_func_t *free_func) {
|
||||
// Each running poller thread has a unique number associated with it, starting
|
||||
// with 0. A garbage entry uses an array of boolean flags, one for each running
|
||||
// thread, to keep track of which threads have seen this entry. Once a garbage
|
||||
// entry has been seen by all threads, the free function is finally called.
|
||||
// This is to make sure that all poller threads have left epoll_wait() after
|
||||
// an fd has been removed from the watch list.
|
||||
|
||||
garbage_t *garb = g_slice_alloc(sizeof(*garb));
|
||||
garb->ptr = ptr;
|
||||
garb->free_func = free_func;
|
||||
|
||||
pthread_mutex_lock(&garbage_lock);
|
||||
|
||||
garb->array_len = g_atomic_int_get(&garbage_thread_num);
|
||||
garb->threads_left = garb->array_len;
|
||||
garb->wait_threads = malloc(sizeof(int) * garb->array_len);
|
||||
memset(garb->wait_threads, 0, sizeof(int) * garb->array_len);
|
||||
|
||||
g_queue_push_tail(&garbage, garb);
|
||||
|
||||
pthread_mutex_unlock(&garbage_lock);
|
||||
}
|
||||
|
||||
|
||||
static void garbage_collect1(garbage_t *garb) {
|
||||
garb->free_func(garb->ptr);
|
||||
|
||||
free(garb->wait_threads);
|
||||
g_slice_free1(sizeof(*garb), garb);
|
||||
}
|
||||
|
||||
|
||||
void garbage_collect(unsigned int num) {
|
||||
dbg("running garbage collection thread %u", num);
|
||||
|
||||
restart:
|
||||
pthread_mutex_lock(&garbage_lock);
|
||||
|
||||
for (GList *l = garbage.head; l; l = l->next) {
|
||||
garbage_t *garb = l->data;
|
||||
// has this been created before we were running?
|
||||
if (garb->array_len <= num)
|
||||
continue;
|
||||
// have we processed this already?
|
||||
if (garb->wait_threads[num])
|
||||
continue;
|
||||
dbg("marking garbage entry %p as seen by %u with %u threads left", garb, num,
|
||||
garb->threads_left);
|
||||
garb->wait_threads[num] = 1;
|
||||
garb->threads_left--;
|
||||
// anything left?
|
||||
if (!garb->threads_left) {
|
||||
// remove from list and process
|
||||
g_queue_delete_link(&garbage, l);
|
||||
pthread_mutex_unlock(&garbage_lock);
|
||||
garbage_collect1(garb);
|
||||
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&garbage_lock);
|
||||
}
|
||||
|
||||
|
||||
void garbage_collect_all(void) {
|
||||
garbage_t *garb;
|
||||
while ((garb = g_queue_pop_head(&garbage)))
|
||||
garbage_collect1(garb);
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
#ifndef _GARBAGE_H_
|
||||
#define _GARBAGE_H_
|
||||
|
||||
typedef void free_func_t(void *);
|
||||
|
||||
unsigned int garbage_new_thread_num(void);
|
||||
void garbage_add(void *ptr, free_func_t *free_func);
|
||||
void garbage_collect(unsigned int num);
|
||||
void garbage_collect_all(void);
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,79 @@
|
||||
#include "inotify.h"
|
||||
#include <sys/inotify.h>
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include "log.h"
|
||||
#include "main.h"
|
||||
#include "epoll.h"
|
||||
#include "metafile.h"
|
||||
|
||||
|
||||
static int inotify_fd = -1;
|
||||
|
||||
|
||||
static handler_func inotify_handler_func;
|
||||
static handler_t inotify_handler = {
|
||||
.func = inotify_handler_func,
|
||||
};
|
||||
|
||||
|
||||
static void inotify_close_write(struct inotify_event *inev) {
|
||||
dbg("inotify close_write(%s)", inev->name);
|
||||
metafile_change(inev->name);
|
||||
}
|
||||
|
||||
|
||||
static void inotify_delete(struct inotify_event *inev) {
|
||||
dbg("inotify delete(%s)", inev->name);
|
||||
metafile_delete(inev->name);
|
||||
}
|
||||
|
||||
|
||||
static void inotify_handler_func(handler_t *handler) {
|
||||
char buf[4 * (sizeof(struct inotify_event) + NAME_MAX + 1)];
|
||||
|
||||
while (1) {
|
||||
int ret = read(inotify_fd, buf, sizeof(buf));
|
||||
if (ret == -1) {
|
||||
if (errno == EINTR || errno == EWOULDBLOCK || errno == EAGAIN)
|
||||
break;
|
||||
die_errno("read on inotify fd failed");
|
||||
}
|
||||
if (ret == 0)
|
||||
die("EOF on inotify fd");
|
||||
|
||||
char *bufend = buf + ret;
|
||||
char *bufhead = buf;
|
||||
while (bufhead < bufend) {
|
||||
struct inotify_event *inev = (void *) bufhead;
|
||||
|
||||
if ((inev->mask & IN_DELETE))
|
||||
inotify_delete(inev);
|
||||
if ((inev->mask & IN_CLOSE_WRITE))
|
||||
inotify_close_write(inev);
|
||||
|
||||
bufhead += sizeof(*inev) + inev->len;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void inotify_setup(void) {
|
||||
inotify_fd = inotify_init1(IN_NONBLOCK);
|
||||
if (inotify_fd == -1)
|
||||
die_errno("inotify_init1 failed");
|
||||
|
||||
int ret = inotify_add_watch(inotify_fd, SPOOL_DIR, IN_CLOSE_WRITE | IN_DELETE);
|
||||
if (ret == -1)
|
||||
die_errno("inotify_add_watch failed");
|
||||
|
||||
if (epoll_add(inotify_fd, EPOLLIN, &inotify_handler))
|
||||
die_errno("failed to add inotify_fd to epoll");
|
||||
}
|
||||
|
||||
|
||||
void inotify_cleanup(void) {
|
||||
close(inotify_fd);
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
#ifndef _INOTIFY_H_
|
||||
#define _INOTIFY_H_
|
||||
|
||||
void inotify_setup(void);
|
||||
void inotify_cleanup(void);
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,15 @@
|
||||
#ifndef _LOG_H_
|
||||
#define _LOG_H_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define die(fmt, ...) do { ilog(LOG_CRIT, "Fatal error: " fmt, ##__VA_ARGS__); exit(-1); } while (0)
|
||||
#define die_errno(msg) die("%s: %s", msg, strerror(errno))
|
||||
#define ilog(fclt, fmt, ...) fprintf(stderr, fmt "\n", ##__VA_ARGS__)
|
||||
#define dbg(fmt, ...) ilog(LOG_DEBUG, fmt, ##__VA_ARGS__)
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,105 @@
|
||||
#include "main.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <glib.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include "log.h"
|
||||
#include "epoll.h"
|
||||
#include "inotify.h"
|
||||
#include "metafile.h"
|
||||
#include "garbage.h"
|
||||
|
||||
|
||||
|
||||
static GQueue threads = G_QUEUE_INIT; // only accessed from main thread
|
||||
|
||||
volatile int shutdown_flag;
|
||||
|
||||
|
||||
static void signals(void) {
|
||||
sigset_t ss;
|
||||
|
||||
sigfillset(&ss);
|
||||
sigdelset(&ss, SIGABRT);
|
||||
sigdelset(&ss, SIGSEGV);
|
||||
sigdelset(&ss, SIGQUIT);
|
||||
sigprocmask(SIG_SETMASK, &ss, NULL);
|
||||
pthread_sigmask(SIG_SETMASK, &ss, NULL);
|
||||
}
|
||||
|
||||
|
||||
static void setup(void) {
|
||||
signals();
|
||||
metafile_setup();
|
||||
epoll_setup();
|
||||
inotify_setup();
|
||||
}
|
||||
|
||||
|
||||
static void start_poller_thread(void) {
|
||||
pthread_t *thr = g_slice_alloc(sizeof(*thr));
|
||||
int ret = pthread_create(thr, NULL, poller_thread,
|
||||
GUINT_TO_POINTER(garbage_new_thread_num()));
|
||||
if (ret)
|
||||
die_errno("pthread_create failed");
|
||||
|
||||
g_queue_push_tail(&threads, thr);
|
||||
}
|
||||
|
||||
|
||||
static void wait_threads_finish(void) {
|
||||
pthread_t *thr;
|
||||
while ((thr = g_queue_pop_head(&threads))) {
|
||||
pthread_cancel(*thr);
|
||||
pthread_join(*thr, NULL);
|
||||
g_slice_free1(sizeof(*thr), thr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void wait_for_signal(void) {
|
||||
sigset_t ss;
|
||||
int ret, sig;
|
||||
|
||||
sigemptyset(&ss);
|
||||
sigaddset(&ss, SIGINT);
|
||||
sigaddset(&ss, SIGTERM);
|
||||
|
||||
while (1) {
|
||||
ret = sigwait(&ss, &sig);
|
||||
if (ret == -1) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
continue;
|
||||
abort();
|
||||
}
|
||||
shutdown_flag = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void cleanup(void) {
|
||||
garbage_collect_all();
|
||||
metafile_cleanup();
|
||||
inotify_cleanup();
|
||||
epoll_cleanup();
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
setup();
|
||||
|
||||
for (int i = 0; i < NUM_THREADS; i++)
|
||||
start_poller_thread();
|
||||
|
||||
wait_for_signal();
|
||||
|
||||
dbg("shutting down");
|
||||
|
||||
wait_threads_finish();
|
||||
|
||||
cleanup();
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
#ifndef _MAIN_H_
|
||||
#define _MAIN_H_
|
||||
|
||||
|
||||
#define SPOOL_DIR "/var/spool/rtpengine"
|
||||
#define PROC_DIR "/proc/rtpengine/0/calls"
|
||||
#define NUM_THREADS 8
|
||||
|
||||
|
||||
extern volatile int shutdown_flag;
|
||||
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,241 @@
|
||||
#include "metafile.h"
|
||||
#include <glib.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include "log.h"
|
||||
#include "stream.h"
|
||||
#include "garbage.h"
|
||||
#include "main.h"
|
||||
#include "aux.h"
|
||||
|
||||
|
||||
static pthread_mutex_t metafiles_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static GHashTable *metafiles;
|
||||
|
||||
//static pcre_t stream_interface_re,
|
||||
//stream_details_re;
|
||||
|
||||
|
||||
static void meta_free(void *ptr) {
|
||||
metafile_t *mf = ptr;
|
||||
|
||||
dbg("freeing metafile info for %s", mf->name);
|
||||
g_string_chunk_free(mf->gsc);
|
||||
for (int i = 0; i < mf->streams->len; i++) {
|
||||
stream_t *stream = g_ptr_array_index(mf->streams, i);
|
||||
stream_close(stream); // should be closed already
|
||||
stream_free(stream);
|
||||
}
|
||||
g_ptr_array_free(mf->streams, TRUE);
|
||||
g_slice_free1(sizeof(*mf), mf);
|
||||
}
|
||||
|
||||
|
||||
// mf is locked
|
||||
static void meta_destroy(metafile_t *mf) {
|
||||
// close all streams
|
||||
for (int i = 0; i < mf->streams->len; i++) {
|
||||
stream_t *stream = g_ptr_array_index(mf->streams, i);
|
||||
pthread_mutex_lock(&stream->lock);
|
||||
stream_close(stream);
|
||||
pthread_mutex_unlock(&stream->lock);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// mf is locked
|
||||
static void meta_stream_interface(metafile_t *mf, unsigned long snum, char *content) {
|
||||
dbg("stream %lu interface %s", snum, content);
|
||||
stream_open(mf, snum, content);
|
||||
}
|
||||
|
||||
|
||||
// mf is locked
|
||||
static void meta_stream_details(metafile_t *mf, unsigned long snum, char *content) {
|
||||
dbg("stream %lu details %s", snum, content);
|
||||
}
|
||||
|
||||
|
||||
// mf is locked
|
||||
static void meta_rtp_payload_type(metafile_t *mf, unsigned long mnum, unsigned int payload_num,
|
||||
char *payload_type)
|
||||
{
|
||||
dbg("payload type in media %lu num %u is %s", mnum, payload_num, payload_type);
|
||||
}
|
||||
|
||||
|
||||
// mf is locked
|
||||
static void meta_section(metafile_t *mf, char *section, char *content, unsigned long len) {
|
||||
unsigned long lu;
|
||||
unsigned int u;
|
||||
|
||||
if (!strcmp(section, "CALL-ID"))
|
||||
mf->call_id = g_string_chunk_insert(mf->gsc, content);
|
||||
else if (!strcmp(section, "PARENT"))
|
||||
mf->parent = g_string_chunk_insert(mf->gsc, content);
|
||||
else if (sscanf_match(section, "STREAM %lu interface", &lu) == 1)
|
||||
meta_stream_interface(mf, lu, content);
|
||||
else if (sscanf_match(section, "STREAM %lu details", &lu) == 1)
|
||||
meta_stream_details(mf, lu, content);
|
||||
else if (sscanf_match(section, "MEDIA %lu PAYLOAD TYPE %u", &lu, &u) == 2)
|
||||
meta_rtp_payload_type(mf, lu, u, content);
|
||||
}
|
||||
|
||||
|
||||
void metafile_change(char *name) {
|
||||
// get or create metafile metadata
|
||||
pthread_mutex_lock(&metafiles_lock);
|
||||
metafile_t *mf = g_hash_table_lookup(metafiles, name);
|
||||
if (!mf) {
|
||||
dbg("allocating metafile info for %s", name);
|
||||
mf = g_slice_alloc0(sizeof(*mf));
|
||||
mf->gsc = g_string_chunk_new(0);
|
||||
mf->name = g_string_chunk_insert(mf->gsc, name);
|
||||
pthread_mutex_init(&mf->lock, NULL);
|
||||
mf->streams = g_ptr_array_new();
|
||||
g_hash_table_insert(metafiles, mf->name, mf);
|
||||
}
|
||||
// switch locks
|
||||
pthread_mutex_lock(&mf->lock);
|
||||
pthread_mutex_unlock(&metafiles_lock);
|
||||
|
||||
char fnbuf[PATH_MAX];
|
||||
snprintf(fnbuf, sizeof(fnbuf), "%s/%s", SPOOL_DIR, name);
|
||||
|
||||
// open file and seek to last known position
|
||||
int fd = open(fnbuf, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
ilog(LOG_ERR, "Failed to open %s: %s\n", fnbuf, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
lseek(fd, mf->pos, SEEK_SET);
|
||||
|
||||
// read the entire file
|
||||
GString *s = g_string_new(NULL);
|
||||
char buf[1024];
|
||||
while (1) {
|
||||
int ret = read(fd, buf, sizeof(buf));
|
||||
if (ret == 0)
|
||||
break;
|
||||
if (ret == -1)
|
||||
die_errno("read on metadata file failed");
|
||||
g_string_append_len(s, buf, ret);
|
||||
}
|
||||
|
||||
// save read position and close file
|
||||
mf->pos = lseek(fd, 0, SEEK_CUR);
|
||||
close(fd);
|
||||
|
||||
// process contents of metadata file
|
||||
char *head = s->str;
|
||||
char *endp = s->str + s->len;
|
||||
while (head < endp) {
|
||||
// section header
|
||||
char *nl = memchr(head, '\n', endp - head);
|
||||
if (!nl || nl == head) {
|
||||
ilog(LOG_WARN, "Missing section header in %s", name);
|
||||
break;
|
||||
}
|
||||
if (memchr(head, '\0', nl - head)) {
|
||||
ilog(LOG_WARN, "NUL character in section header in %s", name);
|
||||
break;
|
||||
}
|
||||
*(nl++) = '\0';
|
||||
char *section = head;
|
||||
dbg("section %s", section);
|
||||
head = nl;
|
||||
|
||||
// content length
|
||||
nl = memchr(head, ':', endp - head);
|
||||
if (!nl || nl == head) {
|
||||
ilog(LOG_WARN, "Content length for section %s missing in %s", section, name);
|
||||
break;
|
||||
}
|
||||
*(nl++) = '\0';
|
||||
if (*(nl++) != '\n') {
|
||||
ilog(LOG_WARN, "Unterminated content length for section %s in %s", section, name);
|
||||
break;
|
||||
}
|
||||
char *errp;
|
||||
unsigned long slen = strtoul(head, &errp, 10);
|
||||
if (*errp != '\0') {
|
||||
ilog(LOG_WARN, "Invalid content length for section %s in %s", section, name);
|
||||
break;
|
||||
}
|
||||
dbg("content length %lu", slen);
|
||||
head = nl;
|
||||
|
||||
// content
|
||||
if (endp - head < slen) {
|
||||
ilog(LOG_WARN, "Content truncated in section %s in %s", section, name);
|
||||
break;
|
||||
}
|
||||
char *content = head;
|
||||
if (memchr(content, '\0', slen)) {
|
||||
ilog(LOG_WARN, "NUL character in content in section %s in %s", section, name);
|
||||
break;
|
||||
}
|
||||
|
||||
// double newline separator
|
||||
head += slen;
|
||||
if (*head != '\n' || *(head + 1) != '\n') {
|
||||
ilog(LOG_WARN, "Separator missing after section %s in %s", section, name);
|
||||
break;
|
||||
}
|
||||
*head = '\0';
|
||||
head += 2;
|
||||
|
||||
meta_section(mf, section, content, slen);
|
||||
}
|
||||
|
||||
g_string_free(s, TRUE);
|
||||
|
||||
out:
|
||||
pthread_mutex_unlock(&mf->lock);
|
||||
}
|
||||
|
||||
|
||||
void metafile_delete(char *name) {
|
||||
// get metafile metadata
|
||||
pthread_mutex_lock(&metafiles_lock);
|
||||
metafile_t *mf = g_hash_table_lookup(metafiles, name);
|
||||
if (!mf) {
|
||||
// nothing to do
|
||||
pthread_mutex_unlock(&metafiles_lock);
|
||||
return;
|
||||
}
|
||||
// switch locks and remove entry
|
||||
pthread_mutex_lock(&mf->lock);
|
||||
g_hash_table_remove(metafiles, name);
|
||||
pthread_mutex_unlock(&metafiles_lock);
|
||||
|
||||
meta_destroy(mf);
|
||||
|
||||
// add to garbage
|
||||
garbage_add(mf, meta_free);
|
||||
pthread_mutex_unlock(&mf->lock);
|
||||
}
|
||||
|
||||
|
||||
void metafile_setup(void) {
|
||||
metafiles = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
//pcre_build(&stream_interface_re, "^STREAM (\\d+) interface$");
|
||||
//pcre_build(&stream_interface_re, "^STREAM (\\d+) details$");
|
||||
}
|
||||
|
||||
|
||||
void metafile_cleanup(void) {
|
||||
GList *mflist = g_hash_table_get_values(metafiles);
|
||||
for (GList *l = mflist; l; l = l->next) {
|
||||
metafile_t *mf = l->data;
|
||||
meta_destroy(mf);
|
||||
meta_free(mf);
|
||||
|
||||
}
|
||||
g_list_free(mflist);
|
||||
g_hash_table_destroy(metafiles);
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
#ifndef _METAFILE_H_
|
||||
#define _METAFILE_H_
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void metafile_setup(void);
|
||||
void metafile_cleanup(void);
|
||||
|
||||
void metafile_change(char *name);
|
||||
void metafile_delete(char *name);
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,14 @@
|
||||
#include "pcre.h"
|
||||
#include <pcre.h>
|
||||
#include "log.h"
|
||||
|
||||
|
||||
void pcre_build(pcre_t *out, const char *pattern) {
|
||||
const char *errptr;
|
||||
int erroff;
|
||||
|
||||
out->re = pcre_compile(pattern, PCRE_DOLLAR_ENDONLY | PCRE_DOTALL, &errptr, &erroff, NULL);
|
||||
if (!out->re)
|
||||
die("Failed to compile PCRE '%s': %s (at %i)", pattern, errptr, erroff);
|
||||
out->extra = pcre_study(out->re, 0, &errptr);
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
#ifndef _PCRE_H_
|
||||
#define _PCRE_H_
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void pcre_build(pcre_t *out, const char *pattern);
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,95 @@
|
||||
#include "stream.h"
|
||||
#include <glib.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <limits.h>
|
||||
#include <fcntl.h>
|
||||
#include "metafile.h"
|
||||
#include "epoll.h"
|
||||
#include "log.h"
|
||||
#include "main.h"
|
||||
|
||||
|
||||
// stream is locked
|
||||
void stream_close(stream_t *stream) {
|
||||
if (stream->fd == -1)
|
||||
return;
|
||||
epoll_del(stream->fd);
|
||||
close(stream->fd);
|
||||
stream->fd = -1;
|
||||
}
|
||||
|
||||
void stream_free(stream_t *stream) {
|
||||
g_slice_free1(sizeof(*stream), stream);
|
||||
}
|
||||
|
||||
|
||||
static void stream_handler(handler_t *handler) {
|
||||
stream_t *stream = handler->ptr;
|
||||
|
||||
//dbg("poll event for %s", stream->name);
|
||||
|
||||
pthread_mutex_lock(&stream->lock);
|
||||
|
||||
if (stream->fd == -1)
|
||||
goto out;
|
||||
|
||||
char buf[65535];
|
||||
int ret = read(stream->fd, buf, sizeof(buf));
|
||||
if (ret == 0) {
|
||||
ilog(LOG_INFO, "EOF on stream %s", stream->name);
|
||||
stream_close(stream);
|
||||
goto out;
|
||||
}
|
||||
else if (ret < 0) {
|
||||
ilog(LOG_INFO, "Read error on stream %s: %s", stream->name, strerror(errno));
|
||||
stream_close(stream);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
pthread_mutex_unlock(&stream->lock);
|
||||
}
|
||||
|
||||
|
||||
// mf is locked
|
||||
static stream_t *stream_get(metafile_t *mf, unsigned long id) {
|
||||
if (mf->streams->len <= id)
|
||||
g_ptr_array_set_size(mf->streams, id + 1);
|
||||
stream_t *ret = g_ptr_array_index(mf->streams, id);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = g_slice_alloc0(sizeof(*ret));
|
||||
g_ptr_array_index(mf->streams, id) = ret;
|
||||
pthread_mutex_init(&ret->lock, NULL);
|
||||
ret->fd = -1;
|
||||
ret->id = id;
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
// mf is locked
|
||||
void stream_open(metafile_t *mf, unsigned long id, char *name) {
|
||||
dbg("opening stream %lu/%s", id, name);
|
||||
|
||||
stream_t *stream = stream_get(mf, id);
|
||||
|
||||
stream->name = g_string_chunk_insert(mf->gsc, name);
|
||||
|
||||
char fnbuf[PATH_MAX];
|
||||
snprintf(fnbuf, sizeof(fnbuf), "%s/%s/%s", PROC_DIR, mf->parent, name);
|
||||
|
||||
stream->fd = open(fnbuf, O_RDONLY | O_NONBLOCK);
|
||||
if (stream->fd == -1) {
|
||||
ilog(LOG_ERR, "Failed to open kernel stream %s: %s", fnbuf, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
// add to epoll
|
||||
stream->handler.ptr = stream;
|
||||
stream->handler.func = stream_handler;
|
||||
epoll_add(stream->fd, EPOLLIN, &stream->handler);
|
||||
}
|
||||
@ -0,0 +1,10 @@
|
||||
#ifndef _STREAM_H_
|
||||
#define _STREAM_H_
|
||||
|
||||
#include "types.h"
|
||||
|
||||
void stream_open(metafile_t *mf, unsigned long id, char *name);
|
||||
void stream_close(stream_t *stream);
|
||||
void stream_free(stream_t *stream);
|
||||
|
||||
#endif
|
||||
@ -0,0 +1,46 @@
|
||||
#ifndef _TYPES_H_
|
||||
#define _TYPES_H_
|
||||
|
||||
|
||||
#include <pthread.h>
|
||||
#include <sys/types.h>
|
||||
#include <glib.h>
|
||||
#include <pcre.h>
|
||||
|
||||
|
||||
typedef struct handler_s handler_t;
|
||||
typedef void handler_func(handler_t *);
|
||||
|
||||
struct handler_s {
|
||||
handler_func *func;
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
struct stream_s {
|
||||
pthread_mutex_t lock;
|
||||
char *name;
|
||||
unsigned long id;
|
||||
int fd;
|
||||
handler_t handler;
|
||||
};
|
||||
typedef struct stream_s stream_t;
|
||||
|
||||
struct metafile_s {
|
||||
pthread_mutex_t lock;
|
||||
char *name;
|
||||
char *parent;
|
||||
char *call_id;
|
||||
off_t pos;
|
||||
GStringChunk *gsc; // XXX limit max size
|
||||
GPtrArray *streams;
|
||||
};
|
||||
typedef struct metafile_s metafile_t;
|
||||
|
||||
// struct pcre_s {
|
||||
// pcre *re;
|
||||
// pcre_extra *extra;
|
||||
// };
|
||||
// typedef struct pcre_s pcre_t;
|
||||
|
||||
|
||||
#endif
|
||||
Loading…
Reference in new issue