diff options
author | Kostyantyn Ovechko <fastinetserver@gmail.com> | 2010-07-30 22:41:21 +0300 |
---|---|---|
committer | Kostyantyn Ovechko <fastinetserver@gmail.com> | 2010-07-30 22:41:21 +0300 |
commit | 7c6f655c2e65eb0644ecec6f1bd508c954862711 (patch) | |
tree | fafb1f2a5baffe65e765b0a05d92b48ad561aa64 | |
parent | Add options: GENERAL_LOG_TIME_FORMAT, ERROR_LOG_TIME_FORMAT and DEBUG_LOG_TIM... (diff) | |
download | idfetch-7c6f655c2e65eb0644ecec6f1bd508c954862711.tar.gz idfetch-7c6f655c2e65eb0644ecec6f1bd508c954862711.tar.bz2 idfetch-7c6f655c2e65eb0644ecec6f1bd508c954862711.zip |
Add [scripting_and_scheduling] section to segget.conf file.
[scripting_and_scheduling]
Segget provides Python scripting functionalyty to support scheduling.
Each time segget tries to start a new connection certain network it calls
a python script (client.py) to accept or reject this connection and
if necessary adjusts its settings.
PYTHON_PATH
Define path to python
Default:
python_path=/usr/bin/python
SCRIPTS_DIR
Define a path to the dir with python scripts. Before establishing connection for
a particular segment via network# segget checks SCRIPTS_DIR.
If SCRIPTS_DIR contains net#.py file, segget will launch schedule() function
from this file to apply settings for connetion and accept or reject this
segment for the moment. net#.py file is a python script file
with a user-writen schedule() function.
It's necessary to import functions before using get("variable"),
set("variable",value), accept_segment() and reject_segment() in schedule().
get() function can obtain values for the following variables:
connection.num, connection.url, connection.max_speed_limit,
network.num, network.mode, network.active_connections_count,
distfile.name, distfile.size, distfile.dld_segments_count,
distfile.segments_count, distfile.active_connections_count,
segment.num, segment.try_num, segment.size, segment.range
set() function can change connection.max_speed_limit, see example:
-----------------EXAMPLE STARTS-----------------
from functions import *
import time;
def schedule():
localtime = time.localtime(time.time());
hour=localtime[3];
# disable downloading distfiles that have size more than 5 000 000 bytes
# from 8-00 to 22-00.
if hour>8 and hour<22 and (get("distfile.size"))>5000000:
print "reject because distfile is too big"
reject_segment()
# set speed limit 50 000 cps for distfiles larger than 1 000 000 bytes
if get("distfile.size")>1000000:
print "limit connection speed"
set(connection.max_speed_limit, 50000)
accept_segment()
-----------------EXAMPLE ENDS-----------------
From example above localtime returns following tuple:
Index Attributes Values
0 tm_year e.i.: 2008
1 tm_mon 1 to 12
2 tm_mday 1 to 31
3 tm_hour 0 to 23
4 tm_min 0 to 59
5 tm_sec 0 to 61 (60 or 61 are leap-seconds)
6 tm_wday 0 to 6 (0 is Monday)
7 tm_yday 1 to 366 (Julian day)
8 tm_isdst -1, 0, 1, -1 means library determines DST
Therefore localtime[3] provides hours.
Segment will be accecpted by default if it was neither accepted nor rejected
during the schedule() function.
sagget saves logs of resulting stdout and stderr in the log folder
separatly for each network. Hence, if there's an error in net3.py file python
error message would be saved to net3_script_stderr.log. Results of print would
be saved in net3_script_stdout.log.
Default:
scripts_dir=./scripts
SCRIPT_SOCKET_PATH
Segget uses AF_UNIX domain sockets for communication with python.
Specify path for the socket on your filesystem.
Default:
script_socket_path=/tmp/segget_script_socket
-rw-r--r-- | segget/connection.cpp | 51 | ||||
-rw-r--r-- | segget/connection.h | 15 | ||||
-rw-r--r-- | segget/distfile.cpp | 15 | ||||
-rw-r--r-- | segget/distfile.h | 2 | ||||
-rw-r--r-- | segget/network.cpp | 10 | ||||
-rw-r--r-- | segget/requestserver.cpp | 2 | ||||
-rw-r--r-- | segget/response.h | 3 | ||||
-rw-r--r-- | segget/scripts/client.py | 7 | ||||
-rw-r--r-- | segget/scripts/functions.py | 77 | ||||
-rw-r--r-- | segget/scripts/net0.py | 15 | ||||
-rw-r--r-- | segget/scriptserver.cpp | 320 | ||||
-rw-r--r-- | segget/scriptserver.h | 73 | ||||
-rw-r--r-- | segget/segget.conf | 75 | ||||
-rw-r--r-- | segget/segget.cpp | 20 | ||||
-rw-r--r-- | segget/segget.h | 1 | ||||
-rw-r--r-- | segget/settings.cpp | 3 | ||||
-rw-r--r-- | segget/settings.h | 6 |
17 files changed, 662 insertions, 33 deletions
diff --git a/segget/connection.cpp b/segget/connection.cpp index 9380cf4..2f8399c 100644 --- a/segget/connection.cpp +++ b/segget/connection.cpp @@ -25,6 +25,7 @@ */ #include "connection.h" +long script_waiting_connection_num=-1; uint Tconnection::total_connections=0; Tconnection connection_array[MAX_CONNECTS]; time_t prev_time; @@ -35,9 +36,8 @@ void init_connections(){ }; } -void Tconnection::start(CURLM *cm, uint network_number, uint distfile_num, Tsegment *started_segment, uint best_mirror_num){ +int Tconnection::start(CURLM *cm, uint network_number, uint distfile_num, Tsegment *started_segment, uint best_mirror_num){ try{ - stats.active_connections_counter++; segment=started_segment; debug("Starting connection for distfile: "+segment->parent_distfile->name); mirror_num=best_mirror_num; @@ -55,7 +55,6 @@ void Tconnection::start(CURLM *cm, uint network_number, uint distfile_num, Tsegm } Tmirror *Pcurr_mirror; - string url; switch (network_array[network_num].network_mode){ case MODE_REMOTE:{ url=segment->parent_distfile->url_list[mirror_num]; @@ -74,16 +73,24 @@ void Tconnection::start(CURLM *cm, uint network_number, uint distfile_num, Tsegm } debug(" URL:"+url); + if (run_user_python_script(connection_num)){ + return REJECTED_BY_USER_PYTHON_SCRIPT; + } + debug("aaaaa"); Pcurr_mirror->start(); debug("bbbbb"); network_array[network_num].connect(); debug("ccccc"); + + stats.active_connections_counter++; segment->prepare_for_connection(cm, connection_num, network_num, distfile_num, url); debug("Started connection for distfile: "+segment->parent_distfile->name); + return 0; }catch(...){ error_log("Error in connection.cpp: start()"); } + return ERROR_WHILE_PREPARING_CONNECTION; } /* string explain_curl_error(int error_code){ @@ -98,6 +105,17 @@ string explain_curl_error(int error_code){ void Tconnection::stop(CURLcode connection_result){ try{ stats.active_connections_counter--; + Tmirror *Pcurr_mirror; + switch (network_array[network_num].network_mode){ + case MODE_REMOTE: + case MODE_CORAL_CDN:{ + Pcurr_mirror=find_mirror(strip_mirror_name(url)); + break; + } + default:{ + Pcurr_mirror=&network_array[network_num].benchmarked_mirror_list[mirror_num]; + } + } debug("Finished connection for distfile: "+segment->parent_distfile->name+" Segment#:"+toString(segment->segment_num)+" Network#"+toString(network_num)+" Status: "+toString(connection_result)); if (connection_result){ string error_str=curl_easy_strerror(connection_result); @@ -105,21 +123,26 @@ void Tconnection::stop(CURLcode connection_result){ error_log("Finished connection for distfile: "+segment->parent_distfile->name+" Segment#:"+toString(segment->segment_num)+" Network#"+toString(network_num)+" Status: "+toString(connection_result)); error_log(" ERROR "+toString(connection_result)+": "+error_str); } - - msg_clean_connection(connection_num); active=false; network_array[network_num].disconnect(); // network_array[network_num].benchmarked_mirror_list[mirror_num].stop(); segment->segment_file.close(); if (connection_result==0){ if (! segment->segment_verification_is_ok()){ + connection_result=CURLE_READ_ERROR; + Pcurr_mirror->stop(time_left_from(connection_array[connection_num].start_time),0); debug("curl_lies - there is a problem downloading segment"); error_log("curl_lies - there is a problem downloading segment"); - connection_result=CURLE_READ_ERROR; + }else{ + Pcurr_mirror->stop(time_left_from(connection_array[connection_num].start_time),segment->segment_size); } + }else{ + Pcurr_mirror->stop(time_left_from(connection_array[connection_num].start_time),0); } - segment->parent_distfile->active_connections_num--; + + msg_clean_connection(connection_num); + /* Tmirror *Pcurr_mirror; if (network_array[network_num].network_mode==MODE_LOCAL){ @@ -131,18 +154,6 @@ void Tconnection::stop(CURLcode connection_result){ } */ - Tmirror *Pcurr_mirror; - switch (network_array[network_num].network_mode){ - case MODE_REMOTE: - case MODE_CORAL_CDN:{ - Pcurr_mirror=find_mirror(strip_mirror_name(segment->url)); - break; - } - default:{ - Pcurr_mirror=&network_array[network_num].benchmarked_mirror_list[mirror_num]; - } - } - timeval now_time; gettimeofday(&now_time,NULL); @@ -170,7 +181,6 @@ void Tconnection::stop(CURLcode connection_result){ // error -> start downloading again // msg_status2(segment->connection_num, toString(connection_result)+"]- Failed download "+segment->file_name); debug(toString(connection_result)+"]- Failed download "+segment->url); - Pcurr_mirror->stop(time_left_from(connection_array[connection_num].start_time),0); if (segment->try_num>=settings.max_tries){ segment->status=SFAILED; segment->parent_distfile->status=DFAILED; @@ -183,7 +193,6 @@ void Tconnection::stop(CURLcode connection_result){ log("Succesfully downloaded "+segment->file_name+" on connection#"+toString(connection_num)); debug(" Successful download "+segment->url); // already done earlier in this function Pcurr_mirror=find_mirror(strip_mirror_name(segment->url)); - Pcurr_mirror->stop(time_left_from(connection_array[connection_num].start_time),segment->segment_size); segment->status=SDOWNLOADED; segment->parent_distfile->inc_dld_segments_count(segment); }; diff --git a/segget/connection.h b/segget/connection.h index bdb4a59..a758ee6 100644 --- a/segget/connection.h +++ b/segget/connection.h @@ -33,6 +33,7 @@ class Tsegment; #include "segment.h" #include "utils.h" #include "networkbroker.h" +#include "scriptserver.h" using namespace std; @@ -40,12 +41,14 @@ class Tconnection{ static uint total_connections; private: Tnetwork_distfile_broker_phases connection_start_time_network_phase_for_pf_networks; + public: uint network_num; uint mirror_num; - public: + string url; ulong total_dld_bytes; ulong bytes_per_last_interval; uint connection_num; + ulong max_speed_limit; bool active; timeval start_time; Tsegment *segment; @@ -53,18 +56,24 @@ class Tconnection{ connection_start_time_network_phase_for_pf_networks(E_USE_AS_LOCAL_MIRRORS), network_num(0), mirror_num(0), + url(""), total_dld_bytes(0), bytes_per_last_interval(0), connection_num(0), + max_speed_limit(0), active(0), start_time(), - segment(0){}; - void start(CURLM *cm, uint network_number, uint distfile_num, Tsegment *started_segment, uint best_mirror_num); + segment(0) + {}; + Tconnection(const Tconnection &L); // copy constructor + Tconnection & operator=(const Tconnection &L); + int start(CURLM *cm, uint network_number, uint distfile_num, Tsegment *started_segment, uint best_mirror_num); void stop(CURLcode connection_result); void inc_bytes_per_last_interval(ulong new_bytes_count); void show_connection_progress(ulong time_diff); }; +extern long script_waiting_connection_num; extern time_t prev_time; extern Tconnection connection_array[MAX_CONNECTS]; void init_connections(); diff --git a/segget/distfile.cpp b/segget/distfile.cpp index 11b7be7..4f56bbe 100644 --- a/segget/distfile.cpp +++ b/segget/distfile.cpp @@ -340,8 +340,12 @@ bool Tdistfile::choose_best_mirror(CURLM* cm, uint connection_num, uint network_ debug("Downloading from BEST_MIRROR:"+url_str); // Pbest_mirror->start(); // active_connections_num++; - connection_array[connection_num].start(cm, network_num, num, &dn_segments[seg_num], best_mirror_num); - return R_R_DOWNLOAD_STARTED; + int result=connection_array[connection_num].start(cm, network_num, num, &dn_segments[seg_num], best_mirror_num); + if (result){ + return result; + }else{ + return R_R_DOWNLOAD_STARTED; + } } else{ error_log("Can't choose mirror for segment:"+dn_segments[seg_num].file_name); @@ -384,7 +388,12 @@ bool Tdistfile::choose_best_local_mirror(CURLM* cm, uint connection_num, uint ne if (best_mirror_num!=-1){ debug("Downloading from BEST_LOCAL_MIRROR:"+network_array[network_num].benchmarked_mirror_list[best_mirror_num].url); // active_connections_num++; - connection_array[connection_num].start(cm, network_num, num, &dn_segments[seg_num], best_mirror_num); + int result=connection_array[connection_num].start(cm, network_num, num, &dn_segments[seg_num], best_mirror_num); + if (result){ + return result; + }else{ + return R_R_DOWNLOAD_STARTED; + } return R_R_DOWNLOAD_STARTED; } else{ diff --git a/segget/distfile.h b/segget/distfile.h index a14e137..69f20c7 100644 --- a/segget/distfile.h +++ b/segget/distfile.h @@ -91,10 +91,10 @@ long is_symlink_restricted(string distfile_name); class Tdistfile{ private: - uint dld_segments_count; bool choose_best_local_mirror(CURLM* cm, uint connection_num, uint network_num, uint seg_num); bool choose_best_mirror(CURLM* cm, uint connection_num, uint network_num, uint seg_num); public: + uint dld_segments_count; Tnetwork_distfile_broker network_distfile_brokers_array[MAX_NETWORKS]; string json_data; // bool downloaded; diff --git a/segget/network.cpp b/segget/network.cpp index ea417ce..ccde337 100644 --- a/segget/network.cpp +++ b/segget/network.cpp @@ -53,8 +53,12 @@ void Tnetwork::load_mirror_list(){ benchmarked_mirror_list.push_back(cur_mirror); debug("LOCAL_MIRROR_ADDED:"+mirror_line); } - } - catch(...){ + }catch(ifstream::failure e){ + if (!file.eof()){ + error_log("Mirror list file: "+mirror_list_file_name+" was opened, but an error occured while reading from it."); + return; + } + }catch(...){ error_log("Mirror list file: "+mirror_list_file_name+" was opened, but an error occured while reading from it."); } }catch(...){ @@ -106,7 +110,7 @@ void Tnetwork::init(uint priority_value){ { conf.set("network_mirrors","only_local_when_possible",only_local_when_possible); load_mirror_list(); - log("Settings: Network"+toString(network_num)+" local mirror_list size:"+toString(mirror_list.size())); + log("Settings in file:network"+toString(network_num)+"_mirrors.conf local mirror_list size:"+toString(benchmarked_mirror_list.size())); break; }; case MODE_PROXY_FETCHER: diff --git a/segget/requestserver.cpp b/segget/requestserver.cpp index de365d2..fd1111f 100644 --- a/segget/requestserver.cpp +++ b/segget/requestserver.cpp @@ -24,7 +24,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "proxyfetcher.h" +#include "requestserver.h" void *run_request_server(void * ){ try{ diff --git a/segget/response.h b/segget/response.h index 77ec9fe..fbeb4f5 100644 --- a/segget/response.h +++ b/segget/response.h @@ -56,6 +56,9 @@ using namespace std; #define DO_NOT_ALLOW_REMOTE_NETWORKS 204 #define ALLOW_LOWER_PRIORITY_NETWORKS 205 +#define REJECTED_BY_USER_PYTHON_SCRIPT 301 +#define ERROR_WHILE_PREPARING_CONNECTION 302 + int decode_server_response(string server_response); #endif
\ No newline at end of file diff --git a/segget/scripts/client.py b/segget/scripts/client.py new file mode 100644 index 0000000..87b97bf --- /dev/null +++ b/segget/scripts/client.py @@ -0,0 +1,7 @@ +# TCP client example +from functions import * +from net0 import * +#user_script + +schedule() +accept_segment()
\ No newline at end of file diff --git a/segget/scripts/functions.py b/segget/scripts/functions.py new file mode 100644 index 0000000..07fe90a --- /dev/null +++ b/segget/scripts/functions.py @@ -0,0 +1,77 @@ +import sys +import socket + +def get(var_name): + print("GET::"+var_name) + client_socket.send ("g<c>"+var_name) + data = client_socket.recv(512) + print "RECIEVED:" , data + #connection.num, + #connection.max_speed_limit, + #network.num, + #network.active_connections_count, + #distfile.size, + #distfile.dld_segments_count, + #distfile.segments_count, + #distfile.active_connections_count, + #segment.num, + #segment.try_num, + #segment.size, + if ((var_name=="connection.url") or (var_name=="distfile.name") or (var_name=="segment.range")): + return data + else: + return int(data) + +def set(var_name,var_value): + var_value_str=str(var_value); + print("SET::"+var_name+"="+var_value_str) + client_socket.send ("s<c>"+var_name+"<n>"+var_value_str) + data = client_socket.recv(512) + print "RECIEVED:" , data + if (data=="o<r>"): + return 0 + else: + return 1 + +def accept_segment(): + print "Accepting segment" + client_socket.send ("a<c>") + client_socket.close() + sys.exit(0) + +def reject_segment(): + print "Rejecting segment" + client_socket.send ("r<c>") + client_socket.close() + sys.exit(0) +# in case users forget to use quotes +class Tconnection: + num="connection.num" + max_speed_limit="connection.max_speed_limit" + url="connection.url" + +class Tnetwork: + num="network.num" + mode="network.mode" + active_connections_count="network.active_connections_count" + +class Tdistfile: + name="distfile.name" + size="distfile.size" + dld_segments_count="distfile.dld_segments_count" + segments_count="distfile.segments_count" + active_connections_count="distfile.active_connections_count" + +class Tsegment: + num="segment.num" + try_num="segment.try_num" + size="segment.size" + range="segment.range" + +connection=Tconnection +network=Tnetwork +distfile=Tdistfile +segment=Tsegment + +client_socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) +client_socket.connect("/tmp/segget_script_socket")
\ No newline at end of file diff --git a/segget/scripts/net0.py b/segget/scripts/net0.py new file mode 100644 index 0000000..8d87955 --- /dev/null +++ b/segget/scripts/net0.py @@ -0,0 +1,15 @@ +from functions import * +import time; +def schedule(): + localtime = time.localtime(time.time()); + hour=localtime[3]; + # disable downloading distfiles that have size more than 5 000 000 bytes + # from 8-00 to 22-00. + if hour>8 and hour<21 and (get("distfile.size"))>5000000: + print "reject because distfile is too big" + reject_segment() + # set speed limit 50 000 cps for distfiles larger than 1 000 000 bytes + if get("distfile.size")>1000000: + print "limit connection speed" + set(connection.max_speed_limit, 50000) + accept_segment()
\ No newline at end of file diff --git a/segget/scriptserver.cpp b/segget/scriptserver.cpp new file mode 100644 index 0000000..44173d7 --- /dev/null +++ b/segget/scriptserver.cpp @@ -0,0 +1,320 @@ +/* +* Copyright (C) 2010 Robin H.Johnson, Ovechko Kostyantyn <fastinetserver@gmail.com>. +* +* Project: IDFetch. +* Developer: Ovechko Kostyantyn Olexandrovich (Kharkiv State Technical University of Construction and Architecture, Ukraine). +* Mentor: Robin H. Johnson (Gentoo Linux: Developer, Trustee & Infrastructure Lead). +* Mentoring organization: Gentoo Linux. +* Sponsored by GSOC 2010. +* +* This file is part of Segget. +* +* Segget is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* Segget 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 +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with Segget; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "scriptserver.h" + +map<std::string, Tvar_nums> variables_; + +#define RESPONSE_OK "o<r>"; +#define RESPONSE_UNKNOWN_VARIABLE "u<r>"; +#define RESPONSE_ERROR "e<r>"; + +#define ACCEPT_SEGMENT 0; +#define ERROR_HAPPEND_SO_ACCEPT_SEGMENT 0; +#define REJECT_SEGMENT 1; + +FILE * stderr_file; +FILE * stdout_file; +int script_server_sockfd, script_client_sockfd; + +string set(uint connection_num, string var_name_and_value){ + try{ + string var_name, var_value; + if (! split("<n>",var_name_and_value,var_name,var_value)){ + map<string,Tvar_nums>::const_iterator ci = variables_.find(var_name); + if (ci == variables_.end()){ + return RESPONSE_UNKNOWN_VARIABLE; + }else{ + switch (ci->second){ + case CONNECTION_MAX_SPEED_LIMIT:{ + ulong new_max_speed_limit=atol(var_value.c_str()); + if (new_max_speed_limit==0){ + error_log("Error in scriptserver.cpp: set("+var_name+"=" + +var_value+"): Can't convert "+var_value+" to integer"); + return RESPONSE_ERROR; + }else{ + connection_array[connection_num].max_speed_limit=new_max_speed_limit; + debug("set("+var_name+"="+var_value+")"); + return RESPONSE_OK; + } + } + default:{ + error_log("Error in scriptserver.cpp: set("+var_name+"=" + +var_value+"): UNKNOWN VARIABLE"); + return RESPONSE_UNKNOWN_VARIABLE; + } + } + } + } + return RESPONSE_ERROR; + }catch(...){ + error_log("Error in scriptserver.cpp: set()"); + return RESPONSE_ERROR; + } +} + +string get(uint connection_num, string var_name){ + try{ + map<string,Tvar_nums>::const_iterator ci = variables_.find(var_name); + if (ci == variables_.end()){ + return RESPONSE_UNKNOWN_VARIABLE; + }else{ + switch (ci->second){ + case DISTFILE_NAME:{ + return connection_array[connection_num].segment->parent_distfile->name; + } + case DISTFILE_SIZE:{ + return toString(connection_array[connection_num].segment->parent_distfile->size); + } + case DISTFILE_DLD_SEGMENTS_COUNT:{ + return toString(connection_array[connection_num].segment->parent_distfile->dld_segments_count); + } + case DISTFILE_SEGMENTS_COUNT:{ + return toString(connection_array[connection_num].segment->parent_distfile->segments_count); + } + case DISTFILE_ACTIVE_CONNECTIONS_COUNT:{ + return toString(connection_array[connection_num].segment->parent_distfile->active_connections_num); + } + case SEGMENT_NUM:{ + return toString(connection_array[connection_num].segment->segment_num); + } + case SEGMENT_TRY_NUM:{ + return toString(connection_array[connection_num].segment->try_num); + } + case SEGMENT_SIZE:{ + return toString(connection_array[connection_num].segment->segment_size); + } + case SEGMENT_RANGE:{ + return connection_array[connection_num].segment->range; + } + case CONNECTION_NUM:{ + return toString(connection_num); + } + case CONNECTION_URL:{ + return connection_array[connection_num].url; + } + case NETWORK_NUM:{ + return toString(connection_array[connection_num].network_num); + } + case NETWORK_MODE:{ + return toString(network_array[connection_array[connection_num].network_num].network_mode); + } + case NETWORK_ACTIVE_CONNECTIONS_COUNT:{ + return toString(network_array[connection_array[connection_num].network_num].active_connections_num); + } + default: return RESPONSE_UNKNOWN_VARIABLE; //unknown variable + } + } + return RESPONSE_ERROR; + }catch(...){ + error_log("Error in scriptserver.cpp: get()"); + return RESPONSE_ERROR; + } +} +void init_variables(){ + try{ + variables_["connection.num"]=CONNECTION_NUM; + variables_["connection.url"]=CONNECTION_URL; + variables_["connection.max_speed_limit"]=CONNECTION_MAX_SPEED_LIMIT; + variables_["network.num"]=NETWORK_NUM; + variables_["network.mode"]=NETWORK_MODE; + variables_["network.active_connections_count"]=NETWORK_ACTIVE_CONNECTIONS_COUNT; + variables_["distfile.name"]=DISTFILE_NAME; + variables_["distfile.size"]=DISTFILE_SIZE; + variables_["distfile.dld_segments_count"]=DISTFILE_DLD_SEGMENTS_COUNT; + variables_["distfile.segments_count"]=DISTFILE_SEGMENTS_COUNT; + variables_["distfile.active_connections_count"]=DISTFILE_ACTIVE_CONNECTIONS_COUNT; + variables_["segment.num"]=SEGMENT_NUM; + variables_["segment.try_num"]=SEGMENT_TRY_NUM; + variables_["segment.size"]=SEGMENT_SIZE; + variables_["segment.range"]=SEGMENT_RANGE; +// variables_[""]=; + }catch(...){ + error_log("Error in scriptserver.cpp: init_variables()"); + } +} +void send(int fd, string response){ + try{ + if (write(fd, response.c_str(), response.length())!=(int)response.length()){ + error_log("Error in scriptserver.cpp: send(): response msg size and sent data size are different."); + }; + }catch(...){ + error_log("Error in scriptserver.cpp: send()"); + } +} + +void killscript(int pID){ + try{ + debug("Before killing script"); +/* int killReturn = kill( pID, SIGKILL); // Kill child process + if( killReturn == ESRCH){ // pid does not exist + error_log("Python script does not exist!"); + }else if( killReturn == EPERM){ // No permission to send signal + error_log("No permission to kill python script"); + }else debug("Signal to kill python script sent. All Ok!"); +*/ + waitpid(pID, NULL, 0); + close(script_server_sockfd); + close(script_client_sockfd); + debug("After killing script"); + }catch(...){ + error_log("Error in scriptserver.cpp: killscript()"); + } +} + +bool run_user_python_script(uint connection_num){ + pid_t pID; + try{ + init_variables(); + socklen_t server_len, client_len; + struct sockaddr_un server_address; + struct sockaddr_un client_address; + + int result; + fd_set readfds, testfds; + + unlink("/tmp/segget_script_socket"); + // Create and name a socket for the server: + script_server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + server_address.sun_family = AF_UNIX; + strcpy(server_address.sun_path, "/tmp/segget_script_socket"); + server_len = sizeof(server_address); + bind(script_server_sockfd, (struct sockaddr *)&server_address, server_len); + //Create a connection queue and initialize readfds to handle input from server_sockfd: + listen(script_server_sockfd, 5); + FD_ZERO(&readfds); + FD_SET(script_server_sockfd, &readfds); + + pID = fork(); + if (pID == 0){ // child + alarm(2); // 2 second limit for script to execute + stderr_file = fopen((settings.logs_dir+"/net" + +toString(connection_array[connection_num].network_num) + +"_script_stderr.log").c_str(), "a+"); + if(stderr_file) { + dup2(fileno(stderr_file), 2); + fclose(stderr_file); + } + stdout_file = fopen((settings.logs_dir+"/net" + +toString(connection_array[connection_num].network_num) + +"_script_stdout.log").c_str(), "a+"); + if(stdout_file) { + dup2(fileno(stdout_file), 1); + fclose(stdout_file); + } + system((settings.python_path+" /home/mona/idfetcha/scripts/client.py").c_str()); + _exit(0); + }else{ + if (pID < 0){ // failed to fork + error_log("Error in scriptserver.cpp: failed to fork"); + return 0; + } + } + + error_log("Created pid:"+toString(pID)); + // parent + //Now wait for clients and requests. Because you have passed a null pointer as the timeout parameter, no timeout will occur. The program will exit and report an error if select returns a value less than 1: + struct timeval user_script_start_time; + gettimeofday(&user_script_start_time,NULL); + + while(1000>time_left_from(user_script_start_time)) { + int fd; + int nread; + testfds = readfds; + + debug("scriptserver is waiting for connections"); + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + + result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, &timeout); + if(result < 1) { + error_log("Error in scriptserver.cpp: run_script_server(): error on select "); + killscript(pID); + return ERROR_HAPPEND_SO_ACCEPT_SEGMENT; + } + //Once you know you’ve got activity, you can find which descriptor it’s on by checking each in turn using FD_ISSET: + for(fd = 0; fd < FD_SETSIZE; fd++) { + if(FD_ISSET(fd,&testfds)) { + //If the activity is on server_sockfd, it must be a request for a new connection, and you add the associated client_sockfd to the descriptor set: + if (fd==script_server_sockfd){ + debug("new script client - read"); + client_len = sizeof(client_address); + script_client_sockfd = accept(script_server_sockfd, + (struct sockaddr *)&client_address, &client_len); + FD_SET(script_client_sockfd, &readfds); + debug("adding script client on fd:"+toString(script_client_sockfd)); + break; + }else{ + script_client_sockfd=fd; + ioctl(fd, FIONREAD, &nread); + if(nread == 0) { + debug("removing script client from fd:"+toString(fd)); + }else{ + char buffer[100000]=""; + if (nread!=read(fd, &buffer, nread)){ + error_log("Error in scriptserver.cpp : run_script_server(): Not all data has been read from script-client"); + } + string recv_msg=noupper(buffer); + debug("SCRIPT-SERVER RECIVED:"+recv_msg); + string command, arguments, send_response; + if (! split("<c>",recv_msg,command,arguments)){ + switch (command[0]){ + case 'g':{ + debug("getting: "+arguments); + send(fd,get(connection_num,arguments)); + break; + } + case 's':{ + debug("setting: "+arguments); + send(fd,set(connection_num,arguments)); + break; + } + case 'a':{ + debug("accepting segment: "); + killscript(pID); + return ACCEPT_SEGMENT; + } + case 'r':{ + debug("rejecting segment: "); + killscript(pID); + return REJECT_SEGMENT; + } + } + } + } + } + } + } + } + killscript(pID); + return ACCEPT_SEGMENT; + }catch(...){ + error_log("Error in scriptserver.cpp: run_script_server()"); + return ERROR_HAPPEND_SO_ACCEPT_SEGMENT; + } + killscript(pID); +}
\ No newline at end of file diff --git a/segget/scriptserver.h b/segget/scriptserver.h new file mode 100644 index 0000000..4b3c916 --- /dev/null +++ b/segget/scriptserver.h @@ -0,0 +1,73 @@ +/* +* Copyright (C) 2010 Robin H.Johnson, Ovechko Kostyantyn <fastinetserver@gmail.com>. +* +* Project: IDFetch. +* Developer: Ovechko Kostyantyn Olexandrovich (Kharkiv State Technical University of Construction and Architecture, Ukraine). +* Mentor: Robin H. Johnson (Gentoo Linux: Developer, Trustee & Infrastructure Lead). +* Mentoring organization: Gentoo Linux. +* Sponsored by GSOC 2010. +* +* This file is part of Segget. +* +* Segget is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* Segget 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 +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with Segget; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#ifndef __SCRIPTSERVER_H__ +#define __SCRIPTSERVER_H__ + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/socket.h> +#include <stdio.h> +//#include <netinet/in.h> +//#include <arpa/inet.h> +#include <sys/un.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <sys/time.h> +#include <sys/ioctl.h> +#include <string.h> +#include <json/json.h> +#include "tui.h" +#include "pkg.h" + +enum Tvar_nums{ + CONNECTION_NUM, + CONNECTION_URL, + CONNECTION_MAX_SPEED_LIMIT, + NETWORK_NUM, + NETWORK_MODE, + NETWORK_ACTIVE_CONNECTIONS_COUNT, + DISTFILE_NAME, + DISTFILE_SIZE, + DISTFILE_DLD_SEGMENTS_COUNT, + DISTFILE_SEGMENTS_COUNT, + DISTFILE_ACTIVE_CONNECTIONS_COUNT, + SEGMENT_NUM, + SEGMENT_TRY_NUM, + SEGMENT_SIZE, + SEGMENT_RANGE +}; + +extern map<std::string, Tvar_nums> variables_; + +extern FILE *stdin; +extern FILE *stdout; +extern FILE *stderr; + +//void *run_script_server(void * ); +bool run_user_python_script(uint connection_num); +#endif
\ No newline at end of file diff --git a/segget/segget.conf b/segget/segget.conf index 4cac4a3..616ce1e 100644 --- a/segget/segget.conf +++ b/segget/segget.conf @@ -279,6 +279,81 @@ request_ip=127.0.0.1 # request_port=10000 request_port=10000 +[scripting_and_scheduling] +# Segget provides Python scripting functionalyty to support scheduling. +# Each time segget tries to start a new connection certain network it calls +# a python script (client.py) to accept or reject this connection and +# if necessary adjusts its settings. + +# PYTHON_PATH +# Define path to python +# Default: +# python_path=/usr/bin/python +python_path=/usr/bin/python + +# SCRIPTS_DIR +# Define path to a dir with python scripts. Before establishing connection for +# a particular segment via network# segget checks SCRIPTS_DIR. +# If SCRIPTS_DIR contains net#.py file, segget will launch schedule() function +# from this file to apply settings for connetion and accept or reject this +# segment for the moment. net#.py file is a causual python script file +# with a user-writen schedule() function. +# It's necessary to import functions before using get("variable"), +# set("variable",value), accept_segment() and reject_segment() in schedule(). +# get() function can obtain values for the following variables: +# connection.num, connection.url, connection.max_speed_limit, +# network.num, network.mode, network.active_connections_count, +# distfile.name, distfile.size, distfile.dld_segments_count, +# distfile.segments_count, distfile.active_connections_count, +# segment.num, segment.try_num, segment.size, segment.range +# set() function can change connection.max_speed_limit, see example: +# -----------------EXAMPLE STARTS----------------- +# from functions import * +# import time; +# def schedule(): +# localtime = time.localtime(time.time()); +# hour=localtime[3]; +# # disable downloading distfiles that have size more than 5 000 000 bytes +# # from 8-00 to 22-00. +# if hour>8 and hour<21 and (get("distfile.size"))>5000000: +# print "reject because distfile is too big" +# reject_segment() +# # set speed limit 50 000 cps for distfiles larger than 1 000 000 bytes +# if get("distfile.size")>1000000: +# print "limit connection speed" +# set(connection.max_speed_limit, 50000) +# accept_segment() +# -----------------EXAMPLE ENDS----------------- +# From example above localtime returns following tuple: +# Index Attributes Values +# 0 tm_year e.i.: 2008 +# 1 tm_mon 1 to 12 +# 2 tm_mday 1 to 31 +# 3 tm_hour 0 to 23 +# 4 tm_min 0 to 59 +# 5 tm_sec 0 to 61 (60 or 61 are leap-seconds) +# 6 tm_wday 0 to 6 (0 is Monday) +# 7 tm_yday 1 to 366 (Julian day) +# 8 tm_isdst -1, 0, 1, -1 means library determines DST +# Therefore localtime[3] provides hours. +# Segment will be accecpted by default if it was neither accepted nor rejected +# during the schedule() function. +# sagget saves logs of resulting stdout and stderr in the log folder +# separatly for each network. Hence, if there's an error in net3.py file python +# error message would be saved to net3_script_stderr.log. Results of print would +# be saved in net3_script_stdout.log. +# Default: +# scripts_dir=./scripts +scripts_dir=./scripts + +# script_socket_path +# Segget uses AF_UNIX domain sockets for communication with python. +# Specify path for the socket on your filesystem. +# NOTE !: Default value can NOT be changed yet (option under development). +# Default: +# script_socket_path=/tmp/segget_script_socket +script_socket_path=/tmp/segget_script_socket + [logs] # LOGS_DIR # Define a dir to store log files. diff --git a/segget/segget.cpp b/segget/segget.cpp index dd5a626..7160c60 100644 --- a/segget/segget.cpp +++ b/segget/segget.cpp @@ -336,7 +336,18 @@ void launch_request_server_thread(){ debug_no_msg("request_server_thread launched"); } } - +/* +void launch_script_server_thread(){ +// if (settings.request_ip!="none"){ + pthread_t script_server_thread; + int iret1; + debug_no_msg("Creating script_server_thread."); +// proxy_fetcher_server_thread.init(); + iret1 = pthread_create( &script_server_thread, NULL, run_script_server, (void*) NULL); + debug_no_msg("script_server_thread launched"); +// } +} +*/ void segget_exit(int sig){ try{ endwin(); @@ -394,6 +405,13 @@ int routine(){ }catch(...){ error_log_no_msg("Error in segget.cpp launch_proxy_fetcher_server_thread failed"); } +/* + try{ + launch_script_server_thread(); + }catch(...){ + error_log_no_msg("Error in segget.cpp launch_script_server_thread failed"); + } +*/ try{ launch_proxy_fetcher_server_thread(); }catch(...){ diff --git a/segget/segget.h b/segget/segget.h index 4edf30e..b74c458 100644 --- a/segget/segget.h +++ b/segget/segget.h @@ -56,6 +56,7 @@ #include "ui_server.h" #include "proxyfetcher.h" #include "requestserver.h" +#include "scriptserver.h" using namespace std; diff --git a/segget/settings.cpp b/segget/settings.cpp index e4415c8..8a4a324 100644 --- a/segget/settings.cpp +++ b/segget/settings.cpp @@ -127,6 +127,9 @@ void Tsettings::init(){ conf.set("request_server","request_ip",request_ip); conf.set("request_server","request_port",request_port,1,65535); + conf.set("scripting_and_scheduling","python_path",python_path); + conf.set("scripting_and_scheduling","scripts_dir",scripts_dir); + conf.clear(); }catch(...){ error_log_no_msg("Error in settings.cpp: init()"); diff --git a/segget/settings.h b/segget/settings.h index f7c2e95..38fb115 100644 --- a/segget/settings.h +++ b/segget/settings.h @@ -79,6 +79,9 @@ class Tsettings{ //request_server string request_ip; ulong request_port; + //scripting_and_scheduling + string python_path; + string scripts_dir; //logs string logs_dir; string general_log_file; @@ -129,6 +132,9 @@ class Tsettings{ //request_server request_ip("127.0.0.1"), request_port(10000), + //scripting_and_scheduling + python_path("/usr/bin/python"), + scripts_dir("./scripts"), //logs logs_dir("./logs"), general_log_file("segget.log"), |