/* * rharchive * * rharchive is a simple tcp connection proxy which combines the * features of rinetd and 6tunnel. rharchive supports IPv4 and * IPv6 and also supports connections from IPv6 to IPv4 * endpoints and vice versa. * * * Copyright (C) 2010-2011 Christian Pointner * * This file is part of rharchive. * * rharchive is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * any later version. * * rharchive is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with rharchive. If not, see . */ #include #include #include #include #include #include #include "datatypes.h" #include "options.h" #include "string_list.h" #include "log.h" #include "daemon.h" #include "writer.h" static gboolean bus_call(GstBus *bus, GstMessage *msg, gpointer data) { GMainLoop *loop = (GMainLoop *)data; switch (GST_MESSAGE_TYPE(msg)) { case GST_MESSAGE_EOS: { log_printf(NOTICE, "End of stream"); g_main_loop_quit(loop); break; } case GST_MESSAGE_INFO: { GError *info; gst_message_parse_info(msg, &info, NULL); log_printf(INFO, "%s", info->message); g_error_free(info); break; } case GST_MESSAGE_WARNING: { GError *warning; gst_message_parse_warning(msg, &warning, NULL); log_printf(WARNING, "%s", warning->message); g_error_free(warning); break; } case GST_MESSAGE_ERROR: { GError *error; gst_message_parse_error(msg, &error, NULL); log_printf(ERROR, "%s", error->message); g_error_free(error); g_main_loop_quit(loop); break; } default: break; } return TRUE; } int main_loop(options_t* opt) { log_printf(INFO, "entering main loop"); GMainLoop *loop; GstElement *pipeline, *source; GstBus *bus; writer_t writer; loop = g_main_loop_new(NULL, FALSE); pipeline = gst_pipeline_new("rharchive"); if(!pipeline || !loop) { log_printf(ERROR, "the pipeline/loop object could not be created. Exiting."); return -1; } int ret = writer_init(&writer, opt->name_format_, opt->output_dir_, opt->interval_, opt->offset_); if(ret) { gst_object_unref(GST_OBJECT(pipeline)); gst_object_unref(GST_OBJECT(loop)); return ret; } GError *error = NULL; source = gst_parse_bin_from_description(opt->src_bin_desc_, TRUE, &error); if(!source || error) { log_printf(ERROR, "Source Bin Description Parser Error: %s", error ? error->message : "unknown"); g_error_free(error); gst_object_unref(GST_OBJECT(writer.sink_)); gst_object_unref(GST_OBJECT(pipeline)); gst_object_unref(GST_OBJECT(loop)); return -1; } gst_bin_add_many(GST_BIN(pipeline), source, writer.sink_, NULL); gst_element_link_many(source, writer.sink_, NULL); bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); gst_bus_add_watch(bus, bus_call, loop); gst_object_unref(bus); log_printf(INFO, "Set State: Paused"); gst_element_set_state(pipeline, GST_STATE_PAUSED); log_printf(INFO, "Set State: Playing"); gst_element_set_state(pipeline, GST_STATE_PLAYING); signal_start(loop); ret = writer_start(&writer); if(!ret) { g_main_loop_run(loop); signal_stop(); } log_printf(NOTICE, "Stopping pipeline"); gst_element_set_state (pipeline, GST_STATE_NULL); writer_stop(&writer); gst_object_unref(GST_OBJECT(pipeline)); return ret; } int main(int argc, char* argv[]) { log_init(); options_t opt; int ret = options_parse(&opt, argc, argv); if(ret) { if(ret > 0) fprintf(stderr, "syntax error near: %s\n\n", argv[ret]); if(ret == -2) fprintf(stderr, "memory error on options_parse, exitting\n"); if(ret == -3) options_print_version(); if(ret == -4) fprintf(stderr, "the interval must be bigger than 0\n"); if(ret != -2 && ret != -3 && ret != -4) options_print_usage(); if(ret == -1 || ret == -3) ret = 0; options_clear(&opt); log_close(); exit(ret); } slist_element_t* tmp = opt.log_targets_.first_; while(tmp) { ret = log_add_target(tmp->data_); if(ret) { switch(ret) { case -2: fprintf(stderr, "memory error on log_add_target, exitting\n"); break; case -3: fprintf(stderr, "unknown log target: '%s', exitting\n", (char*)(tmp->data_)); break; case -4: fprintf(stderr, "this log target is only allowed once: '%s', exitting\n", (char*)(tmp->data_)); break; default: fprintf(stderr, "syntax error near: '%s', exitting\n", (char*)(tmp->data_)); break; } options_clear(&opt); log_close(); exit(ret); } tmp = tmp->next_; } log_printf(NOTICE, "just started..."); options_parse_post(&opt); priv_info_t priv; if(opt.username_) if(priv_init(&priv, opt.username_, opt.groupname_)) { options_clear(&opt); log_close(); exit(-1); } FILE* pid_file = NULL; if(opt.pid_file_) { pid_file = fopen(opt.pid_file_, "w"); if(!pid_file) { log_printf(WARNING, "unable to open pid file: %s", strerror(errno)); } } if(opt.chroot_dir_) if(do_chroot(opt.chroot_dir_)) { options_clear(&opt); log_close(); exit(-1); } if(opt.username_) if(priv_drop(&priv)) { options_clear(&opt); log_close(); exit(-1); } if(opt.daemonize_) { pid_t oldpid = getpid(); daemonize(); log_printf(INFO, "running in background now (old pid: %d)", oldpid); } if(pid_file) { pid_t pid = getpid(); fprintf(pid_file, "%d", pid); fclose(pid_file); } signal_init(); gst_init(NULL, NULL); const gchar *nano_str; guint major, minor, micro, nano; gst_version(&major, &minor, µ, &nano); if (nano == 1) nano_str = " (CVS)"; else if (nano == 2) nano_str = " (Prerelease)"; else nano_str = ""; log_printf(NOTICE, "rharchive linked against GStreamer %d.%d.%d%s", major, minor, micro, nano_str); ret = main_loop(&opt); options_clear(&opt); log_printf(NOTICE, "rharchive shutdown"); gst_deinit(); log_close(); return ret; }