From 31298183024bd138ff3200f7db3c1e67031699c4 Mon Sep 17 00:00:00 2001 From: Andrew McNab Date: Thu, 8 Jun 2006 20:58:18 +0000 Subject: [PATCH] Add SiteCast to slashgrid --- org.gridsite.core/src/Makefile | 6 +- org.gridsite.core/src/gridsite.spec | 21 ++- org.gridsite.core/src/htcp.c | 15 +- org.gridsite.core/src/slashgrid.c | 281 ++++++++++++++++++++++++++++++++---- 4 files changed, 282 insertions(+), 41 deletions(-) diff --git a/org.gridsite.core/src/Makefile b/org.gridsite.core/src/Makefile index 193c115..0cb738b 100644 --- a/org.gridsite.core/src/Makefile +++ b/org.gridsite.core/src/Makefile @@ -241,7 +241,7 @@ install: apidoc $(prefix)/share/man/man1 \ $(prefix)/share/man/man8 \ $(prefix)/lib/httpd/modules \ - $(prefix)/share/doc/gridsite-$(PATCH_VERSION) + $(prefix)/share/doc/gridsite-$(MINOR_VERSION) cp -f ../interface/gridsite.h $(prefix)/include cp -f ../interface/gridsite-gacl.h $(prefix)/include cp -f urlencode $(prefix)/bin @@ -265,10 +265,10 @@ install: apidoc ln -sf libgridsite_globus.so.$(PATCH_VERSION) \ $(prefix)/lib/libgridsite_globus.so.$(MINOR_VERSION) cp -f ../CHANGES ../README ../INSTALL ../LICENSE ../VERSION \ - $(prefix)/share/doc/gridsite-$(PATCH_VERSION) + $(prefix)/share/doc/gridsite-$(MINOR_VERSION) cp -f ../doc/*.html ../doc/*.conf ../doc/*.1 ../doc/*.8 ../doc/*.sh \ ../doc/*.spec \ - $(prefix)/share/doc/gridsite-$(VERSION) + $(prefix)/share/doc/gridsite-$(MINOR_VERSION) cp -f ../doc/*.1 $(prefix)/share/man/man1 cp -f ../doc/*.8 $(prefix)/share/man/man8 gzip -f $(prefix)/share/man/man1/*.1 diff --git a/org.gridsite.core/src/gridsite.spec b/org.gridsite.core/src/gridsite.spec index 6a87236..a169c66 100644 --- a/org.gridsite.core/src/gridsite.spec +++ b/org.gridsite.core/src/gridsite.spec @@ -1,5 +1,5 @@ Name: gridsite -Version: %(echo ${MYVERSION:-1.3.x}) +Version: %(echo ${MYVERSION:-1.x.x}) Release: 1%(sed 's/^\([A-Z]\)[^ ]* \([A-Z]\)[^0-9]*\([0-9][^ ]*\).*/\1\2\3/g' /etc/redhat-release | sed 's/[^A-Z,a-z,0-9]//g') Summary: GridSite License: Modified BSD @@ -17,8 +17,7 @@ GridSite adds GSI, VOMS and GACL support to Apache 2.0 (mod_gridsite), a library for manipulating these technologies (libgridsite), and CGI programs for interactive management of HTTP(S) servers (gridsite-admin.cgi) -See %{prefix}/share/doc/gridsite-%{version} and -http://www.gridsite.org/ for details. +See http://www.gridsite.org/ for details. %package shared Group: Development/Libraries @@ -27,6 +26,8 @@ Summary: GridSite shared library and core documentation %description shared GridSite shared library and core documentation +See http://www.gridsite.org/ for details. + %package devel Group: Development/Libraries Summary: GridSite .a libraries and .h headers @@ -34,6 +35,8 @@ Summary: GridSite .a libraries and .h headers %description devel GridSite development libraries +See http://www.gridsite.org/ for details. + %package apache Group: System Environment/Daemons Summary: GridSite mod_gridsite module for Apache httpd @@ -42,6 +45,8 @@ Requires: gridsite-shared %description apache GridSite Apache module and CGI binaries +See http://www.gridsite.org/ for details. + %package commands Group: Applications/Internet Summary: HTTP(S) read/write client and other GridSite commands @@ -53,6 +58,8 @@ servers using HTTP or HTTPS, or to put or delete files or directories onto remote servers using HTTPS. htcp is similar to scp(1), but uses HTTP/HTTPS rather than ssh as its transfer protocol. +See http://www.gridsite.org/ for details. + %package gsexec Group: Applications/Internet Summary: gsexec binary for the Apache HTTP server @@ -64,6 +71,8 @@ executed by SSI pages) as a user other than the 'apache' user. gsexec is a drop-in replacement for suexec, with extended functionality for use with GridSite and Grid Security credentials. +See http://www.gridsite.org/ for details. + %prep %setup @@ -88,8 +97,8 @@ mkdir -p $RPM_BUILD_ROOT/etc/rc.d/init.d if [ -f /usr/include/fuse/fuse.h ] ; then make install-slashgrid prefix=$RPM_BUILD_ROOT/%{prefix} \ - OPENSSL_FLAGS=$OPENSSL_FLAGS \ - OPENSSL_LIBS=$OPENSSL_LIBS FLAVOR_EXT=$FLAVOR_EXT + OPENSSL_FLAGS=$OPENSSL_FLAGS \ + OPENSSL_LIBS=$OPENSSL_LIBS FLAVOR_EXT=$FLAVOR_EXT else echo -e '#!/bin/sh\necho SlashGrid wasnt built since no fuse-devel on build machine)' \ >$RPM_BUILD_ROOT/%{prefix}/sbin/slashgrid @@ -115,7 +124,7 @@ fi %attr(-, root, root) %{prefix}/lib/libgridsite.so %attr(-, root, root) %{prefix}/lib/libgridsite_globus.so.%{version} %attr(-, root, root) %{prefix}/lib/libgridsite_globus.so -%attr(-, root, root) %{prefix}/share/doc/gridsite-%{version} +%attr(-, root, root) %{prefix}/share/doc/gridsite-%(echo ${MYVERSION:-1.x.x} | cut -f1-2 -d.) %files devel %attr(-, root, root) %{prefix}/include/gridsite.h diff --git a/org.gridsite.core/src/htcp.c b/org.gridsite.core/src/htcp.c index 9c8edb7..b682d17 100644 --- a/org.gridsite.core/src/htcp.c +++ b/org.gridsite.core/src/htcp.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2002-5, Andrew McNab, University of Manchester + Copyright (c) 2002-6, Andrew McNab, University of Manchester All rights reserved. Redistribution and use in source and binary forms, with or @@ -783,6 +783,14 @@ int do_ping(struct grst_stream_data *common_data_ptr) sendto(s, request, request_length, 0, (struct sockaddr *) &srv, sizeof(srv)); free(request); + + if (common_data_ptr->verbose > 0) + fprintf(stderr, "UDP/HTCP NOP ping to %d:%d:%d:%d %d\n", + sitecast_groups[i].quad1, + sitecast_groups[i].quad2, + sitecast_groups[i].quad3, + sitecast_groups[i].quad4, + sitecast_groups[i].port); } /* reusing wait_timeval is a Linux-specific feature of select() */ @@ -803,6 +811,10 @@ int do_ping(struct grst_stream_data *common_data_ptr) response_length = recvfrom(s, response, MAXBUF, 0, &from, &fromlen); + if (common_data_ptr->verbose > 0) + fprintf(stderr, "UDP mesg from %s:%d\n", + inet_ntoa(from.sin_addr), ntohs(from.sin_port)); + if ((GRSThtcpMessageParse(&msg, response, response_length) == GRST_RET_OK) && (msg.opcode == GRSThtcpNOPop) && (msg.rr == 1) && @@ -1686,7 +1698,6 @@ int main(int argc, char *argv[]) int c, i, option_index, anyerror; struct stat statbuf; struct grst_stream_data common_data; - struct grst_sitecast_group sitecast_groups[HTCP_SITECAST_GROUPS]; struct passwd *userpasswd; #if (LIBCURL_VERSION_NUM < 0x070908) diff --git a/org.gridsite.core/src/slashgrid.c b/org.gridsite.core/src/slashgrid.c index 0cf8e63..5e6518f 100644 --- a/org.gridsite.core/src/slashgrid.c +++ b/org.gridsite.core/src/slashgrid.c @@ -33,6 +33,7 @@ * This program is part of GridSite: http://www.gridsite.org/ * *------------------------------------------------------------------*/ +#define _GNU_SOURCE #define _XOPEN_SOURCE #include @@ -43,6 +44,8 @@ #include #include #include +#include +#include #include #include #include @@ -53,6 +56,8 @@ #include +#include "gridsite.h" + #define GRST_SLASH_PIDFILE "/var/run/slashgrid.pid" #define GRST_SLASH_HEADERS "/var/spool/slashgrid/headers" @@ -71,6 +76,13 @@ #define GRST_SLASH_BLOCK_SIZE 4096 #define GRST_SLASH_MAX_HANDLES 16 +#define GRST_SLASH_MAX_LOCATION 1024 + +/* maximum number of SiteCast groups */ +#define GRST_SLASH_MAX_GROUPS 10 + +#define GRST_SLASH_HTCP_PORT 777 + #ifndef CURLOPT_WRITEDATA #define CURLOPT_WRITEDATA CURLOPT_FILE #endif @@ -98,7 +110,7 @@ struct grst_dir_list { char *filename; int modified_set; } ; struct grst_request { int retcode; - char *location; + char location[GRST_SLASH_MAX_LOCATION+1]; size_t length; int length_set; time_t modified; @@ -122,7 +134,9 @@ struct grst_handle { pthread_mutex_t mutex; time_t last_used; } handles[GRST_SLASH_MAX_HANDLES]; -int debugmode = 0; +int debugmode = 0; +int number_of_tries = 1, sitecast_domain_len = 0; +char *sitecast_domain = NULL, *sitecast_groups = NULL; size_t headers_callback(void *ptr, size_t size, size_t nmemb, void *p) /* Find the values of the return code, Content-Length, Last-Modified @@ -144,7 +158,8 @@ size_t headers_callback(void *ptr, size_t size, size_t nmemb, void *p) else if (sscanf(s, "HTTP/%f %d ", &f, &(request_data->retcode)) == 2) ; else if (strncmp(s, "Location: ", 10) == 0) { - request_data->location = strdup(&s[10]); + strncpy(request_data->location, &s[10], GRST_SLASH_MAX_LOCATION); + /* the location string is 1 byte longer and zeroed before use */ for (q=request_data->location; *q != '\0'; ++q) if ((*q == '\r') || (*q == '\n')) *q = '\0'; @@ -210,6 +225,146 @@ int debug_callback(CURL *handle, curl_infotype infotype, return 0; } + +int translate_sitecast_url(char **sitecast_url, char *raw_url) +{ + int request_length, response_length, i, ret, s, igroup; + struct sockaddr_in srv, from; + socklen_t fromlen; +#define MAXBUF 8192 + char *request, response[MAXBUF], *p; + GRSThtcpMessage msg; + struct timeval start_timeval, wait_timeval; + struct grst_sitecast_group + { unsigned char quad1; unsigned char quad2; + unsigned char quad3; unsigned char quad4; + int port; int timewait; int ttl; } groups[GRST_SLASH_MAX_GROUPS]; + fd_set readsckts; + + p = sitecast_groups; + igroup = -1; + + for (igroup=-1; igroup+1 < GRST_SLASH_MAX_GROUPS;) + { + /* defaults for when sscanf fails to find all parameters */ + + groups[igroup+1].port = GRST_SLASH_HTCP_PORT; + groups[igroup+1].timewait = 1; + groups[igroup+1].ttl = 1; + + ret = sscanf(p, "%d.%d.%d.%d:%d:%d:%d", + &(groups[igroup+1].quad1), + &(groups[igroup+1].quad2), + &(groups[igroup+1].quad3), + &(groups[igroup+1].quad4), + &(groups[igroup+1].port), + &(groups[igroup+1].ttl), + &(groups[igroup+1].timewait)); + + if (ret == 0) break; /* end of list ? */ + + if (ret < 5) + { + syslog(LOG_WARNING, + "Failed parsing multicast group parameter %s\n", p); + return GRST_RET_FAILED; + } + + ++igroup; + + if ((p = index(p, ',')) == NULL) break; + ++p; + } + + if (igroup == -1) + { + syslog(LOG_WARNING, "Failed parsing multicast group parameter %s\n", p); + return GRST_RET_FAILED; + } + + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + { + syslog(LOG_WARNING, "Failed to open SiteCast UDP socket\n"); + return GRST_RET_FAILED; + } + + /* loop through multicast groups since we need to take each + ones timewait into account */ + + gettimeofday(&start_timeval, NULL); + + for (i=0; i <= igroup; ++i) + { + if (debugmode) + syslog(LOG_DEBUG, "Querying multicast group %d.%d.%d.%d:%d:%d:%d\n", + groups[i].quad1, groups[i].quad2, + groups[i].quad3, groups[i].quad4, + groups[i].port, groups[i].ttl, + groups[i].timewait); + + bzero(&srv, sizeof(srv)); + srv.sin_family = AF_INET; + srv.sin_port = htons(groups[i].port); + srv.sin_addr.s_addr = htonl(groups[i].quad1*0x1000000 + + groups[i].quad2*0x10000 + + groups[i].quad3*0x100 + + groups[i].quad4); + + /* send off queries, one for each source file */ + + GRSThtcpTSTrequestMake(&request, &request_length, + (int) (start_timeval.tv_usec), + "GET", raw_url, ""); + + sendto(s, request, request_length, 0, + (struct sockaddr *) &srv, sizeof(srv)); + + free(request); + + /* reusing wait_timeval is a Linux-specific feature of select() */ + wait_timeval.tv_usec = 0; + wait_timeval.tv_sec = groups[i].timewait; + + while ((wait_timeval.tv_sec > 0) || (wait_timeval.tv_usec > 0)) + { + FD_ZERO(&readsckts); + FD_SET(s, &readsckts); + + ret = select(s + 1, &readsckts, NULL, NULL, &wait_timeval); + + if (ret > 0) + { + response_length = recvfrom(s, response, MAXBUF, + 0, &from, &fromlen); + + if ((GRSThtcpMessageParse(&msg, response, response_length) + == GRST_RET_OK) && + (msg.opcode == GRSThtcpTSTop) && (msg.rr == 1) && + (msg.trans_id == (int) start_timeval.tv_usec) && + (msg.resp_hdrs != NULL) && + (GRSThtcpCountstrLen(msg.resp_hdrs) > 12)) + { + /* found one */ + + if (debugmode) + syslog(LOG_DEBUG, "Sitecast %s -> %.*s\n", + raw_url, + GRSThtcpCountstrLen(msg.resp_hdrs) - 12, + &(msg.resp_hdrs->text[10])); + + asprintf(sitecast_url, "%.*s", + GRSThtcpCountstrLen(msg.resp_hdrs) - 12, + &(msg.resp_hdrs->text[10])); + + return GRST_RET_OK; + } + } + } + } + + return GRST_RET_FAILED; +} + char *check_x509_user_proxy(pid_t pid) { int fd; @@ -252,14 +407,15 @@ char *check_x509_user_proxy(pid_t pid) int perform_request(struct grst_request *request_data, struct fuse_context *fuse_ctx) { - int ret, i, j; - char *proxyfile = NULL, *range_header = NULL; + int ret, i, j, itry, ishttps = 0; + char *proxyfile = NULL, *range_header = NULL, *url; struct stat statbuf; struct curl_slist *headers_list = NULL; if (strncmp(request_data->url, "https://", 8) == 0) /* HTTPS options */ { // check for X509_USER_PROXY in that PID's environ too + ishttps = 1; if ((proxyfile = check_x509_user_proxy(fuse_ctx->pid)) == NULL) { @@ -311,7 +467,7 @@ int perform_request(struct grst_request *request_data, /* now lock this handle and recheck settings inside the mutex lock */ - pthread_mutex_lock(&(handles[i].mutex)); + pthread_mutex_lock(&(handles[i].mutex)); /* unlock just before return */ if ((handles[i].curl_handle == NULL) || (handles[i].uid != fuse_ctx->uid) || @@ -364,15 +520,8 @@ int perform_request(struct grst_request *request_data, curl_easy_setopt(handles[i].curl_handle, CURLOPT_SSL_VERIFYPEER, 2); curl_easy_setopt(handles[i].curl_handle, CURLOPT_SSL_VERIFYHOST, 2); } - - if (request_data->method == GRST_SLASH_HEAD) - { - curl_easy_setopt(handles[i].curl_handle, CURLOPT_CUSTOMREQUEST, NULL); - curl_easy_setopt(handles[i].curl_handle, CURLOPT_NOBODY, 1); - curl_easy_setopt(handles[i].curl_handle, CURLOPT_HTTPGET, 0); - curl_easy_setopt(handles[i].curl_handle, CURLOPT_UPLOAD, 0); - } - else if (request_data->method == GRST_SLASH_GET) + + if (request_data->method == GRST_SLASH_GET) { curl_easy_setopt(handles[i].curl_handle, CURLOPT_CUSTOMREQUEST, NULL); curl_easy_setopt(handles[i].curl_handle, CURLOPT_NOBODY, 0); @@ -403,9 +552,15 @@ int perform_request(struct grst_request *request_data, curl_easy_setopt(handles[i].curl_handle, CURLOPT_UPLOAD, 0); curl_easy_setopt(handles[i].curl_handle, CURLOPT_CUSTOMREQUEST, "MOVE"); } - else return CURLE_UNSUPPORTED_PROTOCOL; + else /* default or GRST_SLASH_HEAD */ + { + curl_easy_setopt(handles[i].curl_handle, CURLOPT_CUSTOMREQUEST, NULL); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_NOBODY, 1); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_HTTPGET, 0); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_UPLOAD, 0); + } - curl_easy_setopt(handles[i].curl_handle, CURLOPT_WRITEHEADER, request_data); + curl_easy_setopt(handles[i].curl_handle, CURLOPT_WRITEHEADER, request_data); if (request_data->errorbuffer != NULL) curl_easy_setopt(handles[i].curl_handle, CURLOPT_ERRORBUFFER, @@ -419,8 +574,6 @@ int perform_request(struct grst_request *request_data, curl_easy_setopt(handles[i].curl_handle, CURLOPT_WRITEFUNCTION, request_data->writefunction); curl_easy_setopt(handles[i].curl_handle, CURLOPT_WRITEDATA, request_data->writedata); - curl_easy_setopt(handles[i].curl_handle, CURLOPT_URL, request_data->url); - if ((request_data->start >= 0) && (request_data->finish >= request_data->start)) { @@ -438,8 +591,62 @@ int perform_request(struct grst_request *request_data, } else curl_easy_setopt(handles[i].curl_handle, CURLOPT_HTTPHEADER, NULL); - ret = curl_easy_perform(handles[i].curl_handle); - + /* retry loop */ + + for (itry=1; itry <= number_of_tries; ++itry) + { + request_data->length_set = 0; + request_data->modified_set = 0; + request_data->retcode = 0; + request_data->location[0] = '\0'; + + if ((sitecast_domain != NULL) && + (sitecast_groups != NULL) && + + ((request_data->method == GRST_SLASH_HEAD) || + (request_data->method == GRST_SLASH_GET)) && + + ((!ishttps && + (strncmp(&(request_data->url[7]), sitecast_domain, + sitecast_domain_len) == 0) && + ((request_data->url[7+sitecast_domain_len] == ':') || + (request_data->url[7+sitecast_domain_len] == '/')) ) + || + (ishttps && + (strncmp(&(request_data->url[8]), sitecast_domain, + sitecast_domain_len) == 0) && + ((request_data->url[8+sitecast_domain_len] == ':') || + (request_data->url[8+sitecast_domain_len] == '/')) ) ) ) + { + if (debugmode) + syslog(LOG_DEBUG, "Apply SiteCast to URL %s", request_data->url); + + if (translate_sitecast_url(&url, request_data->url) == + GRST_RET_OK) + { + curl_easy_setopt(handles[i].curl_handle, + CURLOPT_URL, url); + ret = curl_easy_perform(handles[i].curl_handle); + + free(url); + } + else + { + ret = 1; + request_data->retcode = 404; /* HTTP not found */ + } + } + else + { + curl_easy_setopt(handles[i].curl_handle, + CURLOPT_URL, request_data->url); + ret = curl_easy_perform(handles[i].curl_handle); + } + +// tests on whether to retry due to server error / timeout go here... + break; + } + if (headers_list != NULL) curl_slist_free_all(headers_list); if (range_header != NULL) free(range_header); @@ -696,6 +903,7 @@ struct grst_dir_list *index_to_dir_list(char *text, char *source) return list; } +#if 0 static char *GRSThttpUrlMildencode(char *in) /* Return a pointer to a malloc'd string holding a partially URL-encoded version of *in. "Partially" means that A-Z a-z 0-9 . = - _ @ and / @@ -735,6 +943,7 @@ static char *GRSThttpUrlMildencode(char *in) *q = '\0'; return out; } +#endif int read_headers_from_cache(struct fuse_context *fuse_ctx, char *filename, off_t *length, time_t *modified) @@ -1102,16 +1311,15 @@ static int slashgrid_getattr(const char *rawpath, struct stat *stbuf) len = strlen(url); - if ((request_data.location != NULL) && + if ((request_data.location[0] != '\0') && (len + 1 == strlen(request_data.location)) && (request_data.location[len] == '/') && (strncmp(url, request_data.location, len) == 0)) { - request_data.length_set = 0; - request_data.modified_set = 0; - request_data.retcode = 0; - request_data.url = request_data.location; - + free(url); + url = strdup(request_data.location); + request_data.url = url; + thiserror = perform_request(&request_data, &fuse_ctx); if ((thiserror != 0) || @@ -1606,13 +1814,13 @@ int slashgrid_mkdir(const char *path, mode_t mode) int slashgrid_chown(const char *path, uid_t uid, gid_t gid) { - puts("slashgrid_chown - NOP"); + if (debugmode) syslog(LOG_DEBUG, "slashgrid_chown - NOP"); return 0; } int slashgrid_chmod(const char *path, mode_t mode) { - puts("slashgrid_chmod - NOP"); + if (debugmode) syslog(LOG_DEBUG, "slashgrid_chmod - NOP"); return 0; } @@ -1720,7 +1928,20 @@ int main(int argc, char *argv[]) int i, ret; for (i=1; i < argc; ++i) - if (strcmp(argv[i], "--debug") == 0) debugmode = 1; + { + if (strcmp(argv[i], "--debug") == 0) debugmode = 1; + else if ((strcmp(argv[i], "--domain") == 0) && (i + 1 < argc)) + { + sitecast_domain = argv[i+1]; + sitecast_domain_len = strlen(sitecast_domain); + ++i; + } + else if ((strcmp(argv[i], "--groups") == 0) && (i + 1 < argc)) + { + sitecast_groups = argv[i+1]; + ++i; + } + } openlog("slashgrid", 0, LOG_DAEMON); -- 1.8.2.3