summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKostyantyn Ovechko <fastinetserver@gmail.com>2010-07-30 22:41:21 +0300
committerKostyantyn Ovechko <fastinetserver@gmail.com>2010-07-30 22:41:21 +0300
commit7c6f655c2e65eb0644ecec6f1bd508c954862711 (patch)
treefafb1f2a5baffe65e765b0a05d92b48ad561aa64
parentAdd options: GENERAL_LOG_TIME_FORMAT, ERROR_LOG_TIME_FORMAT and DEBUG_LOG_TIM... (diff)
downloadidfetch-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.cpp51
-rw-r--r--segget/connection.h15
-rw-r--r--segget/distfile.cpp15
-rw-r--r--segget/distfile.h2
-rw-r--r--segget/network.cpp10
-rw-r--r--segget/requestserver.cpp2
-rw-r--r--segget/response.h3
-rw-r--r--segget/scripts/client.py7
-rw-r--r--segget/scripts/functions.py77
-rw-r--r--segget/scripts/net0.py15
-rw-r--r--segget/scriptserver.cpp320
-rw-r--r--segget/scriptserver.h73
-rw-r--r--segget/segget.conf75
-rw-r--r--segget/segget.cpp20
-rw-r--r--segget/segget.h1
-rw-r--r--segget/settings.cpp3
-rw-r--r--segget/settings.h6
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"),