/* * 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" 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"); // thread safety... g_main_loop_quit (loop); break; } case GST_MESSAGE_ERROR: { gchar *debug; GError *error; gst_message_parse_error(msg, &error, &debug); g_free(debug); log_printf(ERROR, "%s", error->message); // thread safety... g_error_free(error); g_main_loop_quit(loop); break; } default: break; } return TRUE; } static void on_pad_added (GstElement *element, GstPad *pad, gpointer data) { GstPad *sinkpad; GstElement *decoder = (GstElement *)data; log_printf(INFO, "Dynamic pad created, linking demuxer/decoder"); // thread safety... sinkpad = gst_element_get_static_pad(decoder, "sink"); gst_pad_link(pad, sinkpad); gst_object_unref(sinkpad); } int main_loop(options_t* opt) { log_printf(INFO, "entering main loop"); GMainLoop *loop; GstElement *pipeline, *source, *demuxer, *decoder, *conv, *sink; GstBus *bus; loop = g_main_loop_new (NULL, FALSE); pipeline = gst_pipeline_new("audio-player"); source = gst_element_factory_make("filesrc", "file-source"); demuxer = gst_element_factory_make("oggdemux", "ogg-demuxer"); decoder = gst_element_factory_make("vorbisdec", "vorbis-decoder"); conv = gst_element_factory_make("audioconvert", "converter"); sink = gst_element_factory_make("autoaudiosink", "audio-output"); if(!pipeline || !source || !demuxer || !decoder || !conv || !sink) { log_printf(ERROR, "One element could not be created. Exiting."); return -1; } g_object_set(G_OBJECT(source), "location", "file.ogg", NULL); bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline)); gst_bus_add_watch(bus, bus_call, loop); gst_object_unref(bus); gst_bin_add_many(GST_BIN(pipeline), source, demuxer, decoder, conv, sink, NULL); gst_element_link(source, demuxer); gst_element_link_many(decoder, conv, sink, NULL); g_signal_connect(demuxer, "pad-added", G_CALLBACK(on_pad_added), decoder); log_printf(INFO, "Set State: Playing"); gst_element_set_state(pipeline, GST_STATE_PLAYING); g_main_loop_run (loop); log_printf(INFO, "Stopping pipeline"); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (GST_OBJECT (pipeline)); return 0; } 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 != -2 && ret != -3) 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); } 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); if(!ret) log_printf(NOTICE, "normal shutdown"); else if(ret < 0) log_printf(NOTICE, "shutdown after error"); else log_printf(NOTICE, "shutdown after signal"); gst_deinit(); log_close(); return ret; }