RAW connector cleanup, added configfile support, added setuid support, added some more comments other places

This commit is contained in:
Thulinma 2011-04-10 20:02:04 +02:00 committed by root
parent 2b22834fd8
commit 1b86b9a5ef
5 changed files with 114 additions and 46 deletions

View file

@ -354,4 +354,5 @@ namespace Connector_HTTP{
// Load main server setup file, default port 8080, handler is Connector_HTTP::Connector_HTTP // Load main server setup file, default port 8080, handler is Connector_HTTP::Connector_HTTP
#define DEFAULT_PORT 8080 #define DEFAULT_PORT 8080
#define MAINHANDLER Connector_HTTP::Connector_HTTP #define MAINHANDLER Connector_HTTP::Connector_HTTP
#define CONFIGSECT HTTP
#include "../util/server_setup.cpp" #include "../util/server_setup.cpp"

View file

@ -1,19 +1,12 @@
#include <iostream> #include <iostream>
#include "../util/ddv_socket.h" #include "../util/ddv_socket.h"
#include <string>
#include <vector>
#include <cstdlib>
#include <cstdio>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char ** argv) { int main(int argc, char ** argv) {
if (argc < 2){ if (argc < 2){
std::cout << "Usage: " << argv[0] << " stream_name" << std::endl; std::cout << "Usage: " << argv[0] << " stream_name" << std::endl;
return 1; return 1;
} }
std::string input; std::string input = "/tmp/shared_socket_";
input = "/tmp/shared_socket_";
input += argv[1]; input += argv[1];
DDV::Socket S(input); DDV::Socket S(input);
if (!S.connected()){ if (!S.connected()){
@ -21,10 +14,7 @@ int main(int argc, char ** argv) {
return 1; return 1;
} }
char buffer[50000]; char buffer[50000];
int msg; while(std::cout.good() && S.read(buffer,50000)){std::cout.write(buffer,50000);}
while(std::cout.good() && S.read(buffer,50000)){
std::cout.write(buffer,50000);
}
S.close(); S.close();
return 0; return 0;
} }

View file

@ -171,4 +171,5 @@ namespace Connector_RTMP{
// Load main server setup file, default port 1935, handler is Connector_RTMP::Connector_RTMP // Load main server setup file, default port 1935, handler is Connector_RTMP::Connector_RTMP
#define DEFAULT_PORT 1935 #define DEFAULT_PORT 1935
#define MAINHANDLER Connector_RTMP::Connector_RTMP #define MAINHANDLER Connector_RTMP::Connector_RTMP
#define CONFIGSECT RTMP
#include "../util/server_setup.cpp" #include "../util/server_setup.cpp"

View file

@ -309,6 +309,8 @@ DDV::ServerSocket::ServerSocket(std::string address, bool nonblock){
DDV::Socket DDV::ServerSocket::accept(bool nonblock){ DDV::Socket DDV::ServerSocket::accept(bool nonblock){
if (sock < 0){return DDV::Socket(-1);} if (sock < 0){return DDV::Socket(-1);}
int r = ::accept(sock, 0, 0); int r = ::accept(sock, 0, 0);
//set the socket to be nonblocking, if requested.
//we could do this through accept4 with a flag, but that call is non-standard...
if ((r >= 0) && nonblock){ if ((r >= 0) && nonblock){
int flags = fcntl(r, F_GETFL, 0); int flags = fcntl(r, F_GETFL, 0);
flags |= O_NONBLOCK; flags |= O_NONBLOCK;

View file

@ -1,9 +1,18 @@
#include <signal.h> #include <signal.h>
#include "ddv_socket.h" //DDVTech Socket wrapper #include "ddv_socket.h" //DDVTech Socket wrapper
#include "flv_tag.h" //FLV parsing with DDVTech Socket wrapper #include "flv_tag.h" //FLV parsing with DDVTech Socket wrapper
#include <sys/types.h>
#include <pwd.h>
#include <fstream>
#define defstr(x) #x //converts a define name to string
#define defstrh(x) "[" defstr(x) "]" //converts define name to [string]
DDV::ServerSocket server_socket(-1); DDV::ServerSocket server_socket(-1);
void termination_handler (int signum){ /// Basic signal handler. Disconnects the server_socket if it receives
/// a SIGINT, SIGHUP or SIGTERM signal, but does nothing for SIGPIPE.
/// Disconnecting the server_socket will terminate the main listening loop
/// and cleanly shut down the process.
void signal_handler (int signum){
if (!server_socket.connected()) return; if (!server_socket.connected()) return;
switch (signum){ switch (signum){
case SIGINT: break; case SIGINT: break;
@ -12,14 +21,21 @@ void termination_handler (int signum){
default: return; break; default: return; break;
} }
server_socket.close(); server_socket.close();
} }//signal_handler
/// Generic main entry point and loop for DDV Connectors.
/// This sets up the proper termination handler, checks commandline options,
/// parses config files and opens a listening socket on the requested port.
/// Any incoming connections will be accepted and start up the function MAINHANDLER,
/// which should be #defined before including server_setup.cpp.
/// The default port is set by #define DEFAULT_PORT.
/// The configuration file section is set by #define CONFIGSECT.
int main(int argc, char ** argv){ int main(int argc, char ** argv){
DDV::Socket CONN_fd(-1); DDV::Socket S;//placeholder for incoming connections
//setup signal handler //setup signal handler
struct sigaction new_action; struct sigaction new_action;
new_action.sa_handler = termination_handler; new_action.sa_handler = signal_handler;
sigemptyset (&new_action.sa_mask); sigemptyset (&new_action.sa_mask);
new_action.sa_flags = 0; new_action.sa_flags = 0;
sigaction(SIGINT, &new_action, NULL); sigaction(SIGINT, &new_action, NULL);
@ -27,42 +43,82 @@ int main(int argc, char ** argv){
sigaction(SIGTERM, &new_action, NULL); sigaction(SIGTERM, &new_action, NULL);
sigaction(SIGPIPE, &new_action, NULL); sigaction(SIGPIPE, &new_action, NULL);
//default values
int listen_port = DEFAULT_PORT; int listen_port = DEFAULT_PORT;
bool daemon_mode = true; bool daemon_mode = true;
std::string interface = "0.0.0.0"; std::string interface = "0.0.0.0";
std::string configfile = "/etc/ddvtech.conf";
std::string username = "root";
bool ignore_daemon = false;
bool ignore_interface = false;
bool ignore_port = false;
bool ignore_user = false;
int opt = 0; int opt = 0;
static const char *optString = "np:i:h?"; static const char *optString = "ndp:i:u:c:h?";
static const struct option longOpts[] = { static const struct option longOpts[] = {
{"help",0,0,'h'}, {"help",0,0,'h'},
{"port",1,0,'p'}, {"port",1,0,'p'},
{"interface",1,0,'i'}, {"interface",1,0,'i'},
{"no-daemon",0,0,'n'} {"username",1,0,'u'},
{"no-daemon",0,0,'n'},
{"daemon",0,0,'d'},
{"configfile",1,0,'c'}
}; };
while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){ while ((opt = getopt_long(argc, argv, optString, longOpts, 0)) != -1){
switch (opt){ switch (opt){
case 'p': case 'p': listen_port = atoi(optarg); ignore_port = true; break;
listen_port = atoi(optarg); case 'i': interface = optarg; ignore_interface = true; break;
break; case 'n': daemon_mode = false; ignore_daemon = true; break;
case 'i': case 'd': daemon_mode = true; ignore_daemon = true; break;
interface = optarg; case 'c': configfile = optarg; break;
break; case 'u': username = optarg; ignore_user = true; break;
case 'n':
daemon_mode = false;
break;
case 'h': case 'h':
case '?': case '?':
printf("Options: -h[elp], -?, -n[o-daemon], -p[ort] #\n"); printf("Options: -h[elp], -?, -n[odaemon], -d[aemon], -p[ort] VAL, -i[nterface] VAL, -c[onfigfile] VAL, -u[sername] VAL\n");
printf("Defaults:\n interface: 0.0.0.0\n port: %i\n daemon mode: true\n configfile: /etc/ddvtech.conf\n username: root\n", listen_port);
printf("Username root means no change to UID, no matter what the UID is.\n");
printf("If the configfile exists, it is always loaded first. Commandline settings then overwrite the config file.\n");
printf("\nThis process takes it directives from the %s section of the configfile.\n", defstrh(CONFIGSECT));
return 1; return 1;
break; break;
} }
} }//commandline options parser
std::ifstream conf(configfile.c_str(), std::ifstream::in);
std::string tmpstr;
bool acc_comm = false;
size_t foundeq;
if (conf.fail()){
#if DEBUG >= 3
fprintf(stderr, "Configuration file %s not found - using build-in defaults...\n", configfile.c_str());
#endif
}else{
while (conf.good()){
getline(conf, tmpstr);
if (tmpstr[0] == '['){//new section? check if we care.
if (tmpstr == defstrh(CONFIGSECT)){acc_comm = true;}else{acc_comm = false;}
}else{
if (!acc_comm){break;}//skip all lines in this section if we do not care about it
foundeq = tmpstr.find('=');
if (foundeq != std::string::npos){
if ((tmpstr.substr(0, foundeq) == "port") && !ignore_port){listen_port = atoi(tmpstr.substr(foundeq+1).c_str());}
if ((tmpstr.substr(0, foundeq) == "interface") && !ignore_interface){interface = tmpstr.substr(foundeq+1);}
if ((tmpstr.substr(0, foundeq) == "username") && !ignore_user){username = tmpstr.substr(foundeq+1);}
if ((tmpstr.substr(0, foundeq) == "daemon") && !ignore_daemon){daemon_mode = true;}
if ((tmpstr.substr(0, foundeq) == "nodaemon") && !ignore_daemon){daemon_mode = false;}
}//found equals sign
}//section contents
}//configfile line loop
}//configuration
//setup a new server socket, for the correct interface and port
server_socket = DDV::ServerSocket(listen_port, interface); server_socket = DDV::ServerSocket(listen_port, interface);
#if DEBUG >= 3 #if DEBUG >= 3
fprintf(stderr, "Made a listening socket on %s:%i...\n", interface.c_str(), listen_port); fprintf(stderr, "Made a listening socket on %s:%i...\n", interface.c_str(), listen_port);
#endif #endif
if (server_socket.connected()){ if (server_socket.connected()){
//if setup success, enter daemon mode if requested
if (daemon_mode){ if (daemon_mode){
daemon(1, 0); daemon(1, 0);
#if DEBUG >= 3 #if DEBUG >= 3
@ -75,23 +131,41 @@ int main(int argc, char ** argv){
#endif #endif
return 1; return 1;
} }
int status;
while (server_socket.connected()){ if (username != "root"){
waitpid((pid_t)-1, &status, WNOHANG); struct passwd * user_info = getpwnam(username.c_str());
CONN_fd = server_socket.accept(); if (!user_info){
if (CONN_fd.connected()){ #if DEBUG >= 1
pid_t myid = fork(); fprintf(stderr, "Error: could not setuid %s: could not get PID\n", username.c_str());
if (myid == 0){ #endif
break; return 1;
}else{
if (setuid(user_info->pw_uid) != 0){
#if DEBUG >= 1
fprintf(stderr, "Error: could not setuid %s: not allowed\n", username.c_str());
#endif
}else{ }else{
#if DEBUG >= 3 #if DEBUG >= 3
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, CONN_fd.getSocket()); fprintf(stderr, "Changed user to %s\n", username.c_str());
#endif #endif
} }
} }
} }
if (!server_socket.connected()){
return 0; int status;
} while (server_socket.connected()){
return MAINHANDLER(CONN_fd); while (waitpid((pid_t)-1, &status, WNOHANG) > 0){}//clean up all child processes
} S = server_socket.accept();
if (S.connected()){//check if the new connection is valid
pid_t myid = fork();
if (myid == 0){//if new child, start MAINHANDLER
return MAINHANDLER(S);
}else{//otherwise, do nothing or output debugging text
#if DEBUG >= 3
fprintf(stderr, "Spawned new process %i for socket %i\n", (int)myid, S.getSocket());
#endif
}
}
}//while connected
return 0;
}//main