Index: openacs-4/contrib/misc/smsc/README =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/README,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/README 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,6 @@ +This is the SMS module for AOLserver written by Simon Milward of OpenMSG. + +I have not tested this module yet. Use at your own risk. Simon will add +to this package. + +-Roberto Mello, Dec 2003 Index: openacs-4/contrib/misc/smsc/generic-gsm/Makefile =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/generic-gsm/Makefile,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/generic-gsm/Makefile 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,77 @@ +# @(#) $Header: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/generic-gsm/Makefile,v 1.1 2003/12/02 06:17:38 rmello Exp $ +# + +ifdef INST +NSHOME ?= $(INST) +else +NSHOME ?= ../aolserver +endif + +module = generic-gsm +cvspath = nsd-modules/$(module) +version_ = $(subst .,_,$(version)) +distdir = $(module)-$(version) +distfile = $(distdir).tar.gz + +MOD = generic-gsm.so + +# +# Set the objects to build +# + +OBJS = generic-gsm.o + +include $(NSHOME)/include/Makefile.global + +all: $(MOD) + +%.o: %.c + $(CC) -c $(CFLAGS) -D_TCL82 $< -o $@ + +$(MOD): $(OBJS) + $(RM) $@ + $(LDSO) $(LDSOFLAGS) -o $@ $^ $(MODLIBS) + +install: all + $(RM) $(INSTBIN)/$(MOD) + $(CP) $(MOD) $(INSTBIN) + +clean: + $(RM) $(MOD) $(OBJS) + +clobber: clean + $(RM) *.so *.o *.a *~ + +distclean: clobber + $(RM) TAGS tags core *.gz + +release: check-version-var + cvs rtag -r stable release-$(version_) $(cvspath) + +force-release: check-version-var + cvs rtag -F -r stable release-$(version_) $(cvspath) + +dist: check-version-var $(distfile) + +publish: dist + scp "$(distfile)" open-msg.com:www/aolserver + ssh open-msg.com 'cd www/aolserver/src && rm -rf "./$(module)-"* && tar xvzf "../$(distfile)"' + ssh -t open-msg.com vi www/aolserver/index.html + +$(distfile): + rm -rf work + mkdir work + cd work && cvs -Q export -r "release-$(version_)" \ + -d "$(distdir)" "$(cvspath)" + find work -type f | xargs perl -pi -e 's/\@VER\@/$(version)/g' + ( cd work && tar cvf - "$(distdir)" ) | gzip -9 > "$(distfile)" + rm -rf work + +.PHONY: check-version-var + +check-version-var: + @if [ "$(version)" = "" ]; then \ + echo 1>&2 "\$$version must be set to version number."; \ + exit 1; \ + fi + Index: openacs-4/contrib/misc/smsc/generic-gsm/generic-gsm.c =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/generic-gsm/generic-gsm.c,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/generic-gsm/generic-gsm.c 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,2044 @@ +/* + * This file is part of Generic-gsm. + * + * Generic-gsm is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Generic-gsm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Generic-gsm; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: generic-gsm.c,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +static char rcsid[] = "@(#) $"; + +#include "ns.h" +#include "../qcluster/message.h" +#include "../qcluster/cluster.h" +#include "../qcluster/locks.h" +#include "../smsq/smsq.h" +#include "generic-gsm.h" + +#include + +// #define CFG_DIGICOM_HACK + +int Ns_ModuleVersion = 1; + +static struct q_message_type *g_sms_deliver_type; +static Ns_RWLock g_generic_gsm_links_mutex; +static Tcl_HashTable g_generic_gsm_links; + +/* + * Static functions + */ +static int generic_gsm_tcl_init(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int generic_gsm_tcl_delete(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int generic_gsm_tcl_set_status(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int generic_gsm_tcl_update(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int generic_gsm_tcl_connected_p(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int generic_gsm_add_commands(Tcl_Interp *interp, ClientData data); + +static struct generic_gsm_link * generic_gsm_get_link_ptr(char *link_id); +void generic_gsm_send_logic(struct generic_gsm_link *link_ptr); +void generic_gsm_read_logic(struct generic_gsm_link *link_ptr, + char *text); +void generic_gsm_fail_current_message(struct generic_gsm_link *link_ptr); + +int blocked_recv(int sock, void *msg_buf, int msg_size, + int flags); +int blocked_send(int sock, const void *msg_buf, int msg_size, + int flags); +int blocked_read(int fd, void *msg_buf, int msg_size); +int blocked_write(int fd, const void *msg_buf, int msg_size); + + +/* + *---------------------------------------------------------------------- + * + * Ns_ModuleInit -- + * + * Register the Generic GSM functions with the Tcl interpreter + * + * Results: + * None. + * + * Side effects: + * + *---------------------------------------------------------------------- + */ + +NS_EXPORT int +Ns_ModuleInit(char *server, char *module) +{ + /* + * Initialise the hash table for storage of pointers to link structures. + * Also, initialise the a Read/Write mutex to control access. + */ + Ns_RWLockInit(&g_generic_gsm_links_mutex); + Tcl_InitHashTable(&g_generic_gsm_links, TCL_STRING_KEYS); + + Ns_Log(Notice, "generic_gsm: initialisation complete"); + return Ns_TclInitInterps(server, generic_gsm_add_commands, NULL); +} + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_init -- + * + * Creates a new link strucure, adds it to the links hash table, + * and then enters the Generic GSM link execution routine. This + * Function doesn't exit until the link is taken deleted, or server + * shutdown. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +generic_gsm_init(char *link_id, + struct q_group *in_group_ptr, + struct q_group *out_group_ptr, + int status, + char *device, + int device_timeout_secs, + char *service_centre, + char *sim_pin, + int poll_time_secs, + int post_send_delay_usecs, + int use_pdu_p, + int max_retries, + char *msisdn) { + struct generic_gsm_link *link_ptr; + Tcl_HashEntry *entry; + int new_flag; + + /* + * Create the new link structure, and add it to the hash table of + * current links. + */ + link_ptr = ns_malloc(sizeof(struct generic_gsm_link)); + strcpy(link_ptr->link_id, link_id); + strcpy(link_ptr->device, device); + link_ptr->device_timeout_secs = device_timeout_secs; + strcpy(link_ptr->service_centre, service_centre); + strcpy(link_ptr->sim_pin, sim_pin); + ns_sockpair(link_ptr->msg_pipe); + link_ptr->in_group_ptr = in_group_ptr; + link_ptr->out_group_ptr = out_group_ptr; + link_ptr->sock = -1; + link_ptr->sock = poll_time_secs; + link_ptr->non_read_count = 0; + link_ptr->link_status = status; + link_ptr->status = GSMSTATE_IDLE; + link_ptr->old_status = GSMSTATE_IDLE; + Tcl_InitHashTable(&link_ptr->cmgd_ids, TCL_STRING_KEYS); + link_ptr->poll_time_secs = poll_time_secs; + link_ptr->post_send_delay_usecs = post_send_delay_usecs; + link_ptr->use_pdu_p = use_pdu_p; + link_ptr->max_retries = max_retries; + link_ptr->msg_ptr = NULL; + strcpy(link_ptr->msisdn, msisdn); + + Ns_RWLockWrLock(&g_generic_gsm_links_mutex); + entry = Tcl_CreateHashEntry(&g_generic_gsm_links, link_id, &new_flag); + Tcl_SetHashValue(entry, link_ptr); + Ns_RWLockUnlock(&g_generic_gsm_links_mutex); + + /* + * Execute the Generic GSM link processing loop code. + */ + generic_gsm_link_execute(link_ptr); + + /* + * Remove the link from the hashtable, and free the link structure + * memory. + */ + Ns_RWLockWrLock(&g_generic_gsm_links_mutex); + Tcl_DeleteHashEntry(entry); + Ns_RWLockUnlock(&g_generic_gsm_links_mutex); + ns_free(link_ptr); + + Ns_Log(Notice,"generic_gsm_init: Exiting"); + + return NS_TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_link_execute -- + * + * Code that actually processes the link. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +generic_gsm_link_execute(struct generic_gsm_link *link_ptr) +{ + SOCKET max; + fd_set set; + int finished; + int connected; + unsigned char msg_buf; + int sock_save; + struct timeval tv; + int retval; + struct termios *tty; + /* + * Kick off the reader thread. + */ + Ns_ThreadCreate(generic_gsm_link_reader, (void *) link_ptr, 0, NULL); + Ns_ThreadCreate(generic_gsm_link_event_handler, (void *) link_ptr, + 0, NULL); + + finished = 0; + while (!finished) { + /* + * Loop until we're online, and connected. + */ + connected = 0; + while (!connected && + !finished) { + FD_ZERO(&set); + FD_SET(link_ptr->msg_pipe[0], &set); + max = link_ptr->msg_pipe[0]; + tv.tv_sec = 1; + tv.tv_usec = 0; + retval = select(max+1, &set, NULL, NULL, &tv); + if (retval && + FD_ISSET(link_ptr->msg_pipe[0], &set)) { + /* + * Recieved a message, so lets process it. + */ + if (blocked_recv(link_ptr->msg_pipe[0], &msg_buf, 1, 0) <= 0) { + Ns_Log(Error,"generic_gsm_link_execute: Pipe read error, %s", + ns_sockstrerror(ns_sockerrno)); + finished = 1; + } else { + switch (msg_buf) { + case GENERICGSM_ONLINE: + case GENERICGSM_OFFLINE: + link_ptr->link_status = msg_buf; + break; + case GENERICGSM_DELETE: + Ns_Log(Notice, "generic_gsm_link_execute: Link delete message, setting finished flag"); + finished = 1; + break; + case GENERICGSM_GROUP_ACTIVITY: + break; + default: + Ns_Log(Error,"generic_gsm_link_execute: Unexpected pipe message %d whilst in unconnected loop", msg_buf); + } + } + } else { + /* + * Select timed out, so lets attempt a connection to the + * device (if we're online). + */ + if (link_ptr->link_status == GENERICGSM_ONLINE) { + link_ptr->sock = open(link_ptr->device, O_RDWR | O_NONBLOCK | O_NOCTTY); +// link_ptr->sock = open(link_ptr->device, O_RDWR); + if (link_ptr->sock < 0) { + Ns_Log(Error, + "generic_gsm_link_execute: (Link %s) Unable to open device %s", + link_ptr->link_id, + link_ptr->device); + sleep(1); + } else { +// if (ioctl(link_ptr->sock, TIOCSCTTY, 0) < 0) { +// Ns_Log(Error, +// "generic_gsm_link_execute: ioctl() failed, continuing anyway"); +// } + /* Try and get tty settings */ + tcdrain(link_ptr->sock); + tcflush(link_ptr->sock, TCIOFLUSH); + if (tcgetattr(link_ptr->sock, &link_ptr->tty) < 0) { + Ns_Log(Error, + "generic_gsm_link_execute: togetattr() failed"); + close(link_ptr->sock); + } else { + connected = 1; + tty = &link_ptr->tty; + tty->c_cc[VMIN] - 0; /* nonblocking */ + tty->c_cc[VTIME] = 0; + tty->c_oflag = 0; + tty->c_lflag = 0; + + tty->c_cflag &= ~(CSIZE | CSTOPB | PARENB); + tty->c_cflag |= CS8 | CREAD; + tty->c_cflag |= CLOCAL; /* ignore modem status lines */ + tty->c_lflag = IGNBRK | IGNPAR; + tty->c_lflag &= ~ICANON; /* non-canonical mode */ + tty->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHOKE); + + tty->c_cflag |= CRTSCTS ; /* flow control */ + + cfsetospeed(tty, B9600); + tcdrain(link_ptr->sock); + + if (tcsetattr(link_ptr->sock, TCSANOW, tty) < 0) { + Ns_Log(Error, + "generic_gsm_link_execute: tcsetattr() failed"); + close(link_ptr->sock); + } else { + connected = 1; + } + } + } + } + } + } + + /* + * We're now connected. Send message to Reader thread, and loop + * whilst we're still connected. + * + */ + if (connected && !finished) { + Ns_Log(Notice, + "generic_gsm_link_execute: (Link %s), Connected to device %s (%d)", + link_ptr->link_id, + link_ptr->device, + link_ptr->sock); + msg_buf = GENERICGSM_CONNECTED; + blocked_send(link_ptr->msg_pipe[0], &msg_buf, 1, 0); + } + + link_ptr->old_status = GSMSTATE_IDLE; + link_ptr->status = GSMSTATE_ATE0; + generic_gsm_send_logic(link_ptr); + + while (connected && + !finished) { + /* + * Create select to wait for any messages. + */ + FD_ZERO(&set); + FD_SET(link_ptr->msg_pipe[0], &set); + max = link_ptr->msg_pipe[0]; + tv.tv_sec = link_ptr->poll_time_secs; + tv.tv_usec = 0; + select(max+1, &set, NULL, NULL, &tv); + + if (FD_ISSET(link_ptr->msg_pipe[0], &set)) { + /* + * Another thread sent message. + * Read the signal and process. + */ + if (blocked_recv(link_ptr->msg_pipe[0], &msg_buf, 1, 0) <= 0) { + Ns_Log(Error,"generic_gsm_link_execute: Pipe read error, %s", + ns_sockstrerror(ns_sockerrno)); + } else { + /* + * If the message is a number less than 100, it is deemed a + * signalling message rather than a state change. + */ + if (msg_buf < 100) { + switch (msg_buf) { + case GENERICGSM_ONLINE: + break; + /* + * Offline message, clear the connected flag so we drop out + * the 'connected' loop. + */ + case GENERICGSM_OFFLINE: + case GENERICGSM_DISCONNECTED: + msg_buf = GENERICGSM_OFFLINE; + sock_save = link_ptr->sock; + + link_ptr->sock = -1; + link_ptr->link_status = GENERICGSM_OFFLINE; + link_ptr->status = GSMSTATE_IDLE; + blocked_send(link_ptr->msg_pipe[0], &msg_buf, 1, 0); + close(sock_save); + connected = 0; + break; + /* + * Delete message. Exit the thread. + */ + case GENERICGSM_DELETE: + msg_buf = GENERICGSM_OFFLINE; + sock_save = link_ptr->sock; + + link_ptr->sock = -1; + link_ptr->link_status = GENERICGSM_OFFLINE; + link_ptr->status = GSMSTATE_IDLE; + blocked_send(link_ptr->msg_pipe[0], &msg_buf, 1, 0); + close(sock_save); + connected = 0; + finished = 1; + break; + case GENERICGSM_GROUP_ACTIVITY: + if (link_ptr->status == GSMSTATE_IDLE && + link_ptr->non_read_count < GENERICGSM_MAX_BEFORE_READ) { + link_ptr->status = GSMSTATE_CMGS; + } + generic_gsm_send_logic(link_ptr); + break; + /* + * Default case. Warn against any unexpected messages. + */ + default: + Ns_Log(Error,"generic_gsm_link_reader: Unexpected pipe message %d whilst in connected loop", msg_buf); + break; + } + } else { + /* + * This is a status change message. If this is an IDLE + * message: + * If there are messages to process, and we haven't reached + * the maximum 'messages per read', signal a message send to + * happen. + * Otherwise, signal a message read query to happen. + */ + if (link_ptr->status == GSMSTATE_IDLE && + !smsq_group_empty_p(link_ptr->out_group_ptr) && + link_ptr->non_read_count < GENERICGSM_MAX_BEFORE_READ) { + link_ptr->status = GSMSTATE_CMGS; + } else if (link_ptr->status == GSMSTATE_IDLE && + link_ptr->old_status != GSMSTATE_CMGL_DATA) { + link_ptr->status = GSMSTATE_CMGL; + } + generic_gsm_send_logic(link_ptr); + } + } + } else { + /* + * We've timed out on the select, so lets do a quick check for + * messages on the queue. If any exist, kick off a send, otherwise + * kick off a read message query. + */ + if (link_ptr->status == GSMSTATE_IDLE && + !smsq_group_empty_p(link_ptr->out_group_ptr) && + link_ptr->non_read_count < GENERICGSM_MAX_BEFORE_READ) { + link_ptr->status = GSMSTATE_CMGS; + } else if (link_ptr->status == GSMSTATE_IDLE) { + link_ptr->status = GSMSTATE_CMGL; + } + generic_gsm_send_logic(link_ptr); + } + } + if (link_ptr->msg_ptr != NULL) { + generic_gsm_fail_current_message(link_ptr); + } + /* + * Inform the Reader thread that the connection to the device has + * closed. + */ + msg_buf = GENERICGSM_DISCONNECTED; + blocked_send(link_ptr->msg_pipe[0], &msg_buf, 1, 0); + } + + /* + * Inform the Reader thread that this link is shutting down. + * Wait for a second to make sure threads have time to clean up. + */ + msg_buf = GENERICGSM_DELETE; + blocked_send(link_ptr->msg_pipe[0], &msg_buf, 1, 0); + + return NS_TRUE; +} + +void generic_gsm_send_logic(struct generic_gsm_link *link_ptr) { + struct q_message *msg_ptr; + char bits8_msg[170]; /* Should get away with 160, but just to be safe */ + char send_buffer[1024]; + Tcl_HashSearch cmgd_search; + unsigned char msg_buf; + + switch (link_ptr->status) { + case GSMSTATE_IDLE: + break; + case GSMSTATE_CMGL: + link_ptr->non_read_count = 0; + /* Send CMGL */ + link_ptr->cmgl_status = 0; + if (link_ptr->use_pdu_p) { + sprintf(send_buffer, "AT+CMGL=4\r"); + } else { + sprintf(send_buffer, "AT+CMGL=\"ALL\"\r"); + } + link_ptr->sent_wait_count = 0; + link_ptr->status = GSMSTATE_CMGL_DATA; + blocked_write(link_ptr->sock, send_buffer, + strlen(send_buffer)); + break; + case GSMSTATE_CMGS: + /* + * Try and find another message on the queue. If one exists, + * change state to send it. + */ + msg_ptr = smsq_get_msg_and_set_status(link_ptr->out_group_ptr, + Q_MSG_STATUS_READY, + Q_MSG_STATUS_PROCESSING); + if (msg_ptr != NULL) { + link_ptr->msg_ptr = msg_ptr; + link_ptr->payload_ptr = msg_ptr->msg_ptr; + link_ptr->status = GSMSTATE_CMGS_ARROW; + link_ptr->sent_wait_count = 0; + /* Send CMGS */ + if (link_ptr->use_pdu_p) { + generic_gsm_encode_pdu(link_ptr->pdu_string, + &link_ptr->pdu_length, + link_ptr); + sprintf(send_buffer, "AT+CMGS=%d\r", + link_ptr->pdu_length); + } else { + sprintf(send_buffer, "AT+CMGS=\"%s\"\r", + link_ptr->payload_ptr->dest_address); + } + blocked_write(link_ptr->sock, send_buffer, + strlen(send_buffer)); + } else { + link_ptr->status = GSMSTATE_IDLE; + } + break; + case GSMSTATE_CMGS_MESSAGE: + /* Send CMGS data */ + if (link_ptr->use_pdu_p) { + /* Don't forget the (26) */ + sprintf(send_buffer, "%s%c", + link_ptr->pdu_string, 26); + link_ptr->sent_wait_count = 0; + link_ptr->status = GSMSTATE_CMGS_RESP; + blocked_write(link_ptr->sock, send_buffer, + strlen(send_buffer)); + } else { + gsm0338_unpack_7bit(bits8_msg, link_ptr->payload_ptr->user_data, + link_ptr->payload_ptr->user_data_len, + link_ptr->payload_ptr->unpacked_data_len); + /* Don't forget the (26) */ + sprintf(send_buffer, "%s%c", + bits8_msg, 26); + link_ptr->sent_wait_count = 0; + link_ptr->status = GSMSTATE_CMGS_RESP; + blocked_write(link_ptr->sock, send_buffer, + strlen(send_buffer)); + } + break; + case GSMSTATE_ATE0: + /* Send ATE0 */ + sprintf(send_buffer, "ATE0\r\n"); + link_ptr->sent_wait_count = 0; + link_ptr->status = GSMSTATE_ATE0_ATE0; + blocked_write(link_ptr->sock, send_buffer, + strlen(send_buffer)); + break; + case GSMSTATE_CPIN: + /* Send CPIN */ + sprintf(send_buffer, "AT+CPIN=\"%s\"\r\r", + link_ptr->sim_pin); + link_ptr->sent_wait_count = 0; + link_ptr->status = GSMSTATE_CPIN_OK; + blocked_write(link_ptr->sock, send_buffer, + strlen(send_buffer)); + break; + case GSMSTATE_CSCA: + /* Send CSCA */ + sprintf(send_buffer, "AT+CSCA=\"%s\"\r\r", + link_ptr->service_centre); + link_ptr->sent_wait_count = 0; + link_ptr->status = GSMSTATE_CSCA_OK; + blocked_write(link_ptr->sock, send_buffer, + strlen(send_buffer)); + break; + case GSMSTATE_CMGF: + /* Send CMGF */ + if (link_ptr->use_pdu_p) { + sprintf(send_buffer, "AT+CMGF=0\r\r"); + } else { + sprintf(send_buffer, "AT+CMGF=1\r\r"); + } + link_ptr->sent_wait_count = 0; + link_ptr->status = GSMSTATE_CMGF_OK; + blocked_write(link_ptr->sock, send_buffer, + strlen(send_buffer)); + break; + case GSMSTATE_CNMI: + /* Send SCRT */ + sprintf(send_buffer, "AT+CNMI=1,1\r\r"); + link_ptr->sent_wait_count = 0; + link_ptr->status = GSMSTATE_CNMI_OK; + blocked_write(link_ptr->sock, send_buffer, + strlen(send_buffer)); + break; + case GSMSTATE_CMGD: + /* Send CMGD */ + link_ptr->cmgd_search_ptr = Tcl_FirstHashEntry( + &link_ptr->cmgd_ids, &cmgd_search); + if (link_ptr->cmgd_search_ptr != NULL) { + sprintf(send_buffer, "AT+CMGD=%s\r\r", Tcl_GetHashKey( + &link_ptr->cmgd_ids, + link_ptr->cmgd_search_ptr)); + link_ptr->sent_wait_count = 0; + link_ptr->status = GSMSTATE_CMGD_OK; + blocked_write(link_ptr->sock, send_buffer, + strlen(send_buffer)); + } else { + if (!smsq_group_empty_p(link_ptr->out_group_ptr)) { + link_ptr->status = GSMSTATE_IDLE; + msg_buf = GENERICGSM_GROUP_ACTIVITY; + blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); + } else { + link_ptr->status = GSMSTATE_IDLE; + } + } + break; + default: + break; + } +} + + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_link_reader -- + * + * Reader thread. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static void generic_gsm_link_reader(void *arg) +{ + SOCKET max; + fd_set set; + unsigned char msg_buf; + int finished; + int connected; + int n; + struct generic_gsm_link *link_ptr; + char buffered[1024]; + int buffered_size; + int count; + char *brk_pos; + unsigned char old_status; + struct timeval tv; + + link_ptr = (struct generic_gsm_link *) arg; + + finished = 0; + while (!finished) { + + /* + * Loop until we're connected. + */ + connected = 0; + while (!connected && + !finished) { + FD_ZERO(&set); + FD_SET(link_ptr->msg_pipe[1], &set); + max = link_ptr->msg_pipe[1]; + select(max+1, &set, NULL, NULL, NULL); + if (FD_ISSET(link_ptr->msg_pipe[1], &set)) { + if (blocked_recv(link_ptr->msg_pipe[1], &msg_buf, 1, 0) <= 0) { + Ns_Log(Error,"generic_gsm_link_reader: Pipe read error, %s", + ns_sockstrerror(ns_sockerrno)); + } else { + switch (msg_buf) { + case GENERICGSM_CONNECTED: + connected = 1; + break; + case GENERICGSM_DELETE: + finished = 1; + break; + case GENERICGSM_DISCONNECTED: + case GENERICGSM_OFFLINE: + break; + default: + Ns_Log(Error,"generic_gsm_link_reader: Unexpected pipe message %d whilst in unconnected loop", msg_buf); + } + } + } + } + + /* + * We're now connected. Loop while we're still connected. + */ + while (connected && + !finished) { + /* + * Create select to wait for data. + */ + FD_ZERO(&set); + FD_SET(link_ptr->sock, &set); + FD_SET(link_ptr->msg_pipe[1], &set); + max = link_ptr->sock; + if (link_ptr->msg_pipe[1] > max) { + max = link_ptr->msg_pipe[1]; + } + tv.tv_sec = 1; + tv.tv_usec = 0; + select(max+1, &set, NULL, NULL, &tv); + + if (FD_ISSET(link_ptr->msg_pipe[1], &set)) { + /* + * Another thread sent message. + * Read the signal and process. + */ + if (blocked_recv(link_ptr->msg_pipe[1], &msg_buf, 1, 0) <= 0) { + Ns_Log(Error,"generic_gsm_link_reader: Pipe read error, %s", + ns_sockstrerror(ns_sockerrno)); + } else { + switch (msg_buf) { + /* + * Offline message, clear the connected flag so we drop out + * the 'connected' loop. + */ + case GENERICGSM_OFFLINE: + case GENERICGSM_DISCONNECTED: + connected = 0; + break; + /* + * Delete message. Exit the thread. + */ + case GENERICGSM_DELETE: + finished = 1; + break; + /* + * Default case. Warn against any unexpected messages. + */ + default: + Ns_Log(Error,"generic_gsm_link_reader: Unexpected pipe message %d whilst in connected loop", msg_buf); + break; + } + } + } else if (FD_ISSET(link_ptr->sock, &set)) { + /* + * Read data from the modem. + */ + n = read(link_ptr->sock, buffered + buffered_size, + sizeof(buffered) - buffered_size); + /* + * Check for a read error + */ + if (n < 0) { + msg_buf = GENERICGSM_DISCONNECTED; + blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); + connected = 0; + } else { +// for (count = 0; count < n; count++) { +// Ns_Log(Notice, ">>>> 0x%x", buffered[count + buffered_size]); +// } + buffered_size += n; + buffered[buffered_size] = '\0'; + while (buffered_size > 0 && + ((brk_pos = strpbrk(buffered, "\r\n\f")) != NULL || + !strcmp(buffered, "> "))) { + if (brk_pos == NULL) { + brk_pos = buffered + 2; + buffered_size++; + } + /* + * Ignore empty lines + */ + if (brk_pos > buffered) { + *brk_pos = '\0'; + old_status = link_ptr->status; + link_ptr->old_status = link_ptr->status; + + generic_gsm_read_logic(link_ptr, + buffered); + Ns_Log(Notice, "> \"%s\" (%d -> %d)", buffered, old_status, link_ptr->status); + /* + * For all state changes that require activity from the + * Writer thread, construct and send the pipe message. + */ + switch (link_ptr->status) { + case GSMSTATE_CMGS_ARROW: + case GSMSTATE_CMGS_MESSAGE: + case GSMSTATE_ATE0: + case GSMSTATE_CPIN: + case GSMSTATE_CSCA: + case GSMSTATE_CMGF: + case GSMSTATE_CNMI: + case GSMSTATE_CMGD: + case GSMSTATE_CMGL: + case GSMSTATE_IDLE: + msg_buf = link_ptr->status; + blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); + break; + } + } + if ((buffered_size - (brk_pos - buffered) - 1) > 0) { + memmove(buffered, brk_pos + 1, + buffered_size - (brk_pos - buffered) - 1); + } + buffered_size -= ((brk_pos - buffered) + 1); + buffered[buffered_size] = '\0'; + } + } + } + } + } + + Ns_Log(Notice,"generic_gsm_link_reader: Exiting"); + Ns_ThreadExit (0); + return; +} + +void generic_gsm_read_logic(struct generic_gsm_link *link_ptr, + char *text) { + unsigned char old_status; + char msg_buf; + char cmgl_text[1024]; + char *cur_pos; + char *cmgl_msg_num; + char *cmgl_msisdn; + char msg_id[128]; + Tcl_HashEntry *entry; + int new_flag; + char msisdn[128]; + unsigned char msg_data[256]; + int msg_data_length; + int unpacked_size; + int data_coding_scheme; + struct smsc_tm smsc_timestamp; + + old_status = link_ptr->status; + switch (link_ptr->status) { + case GSMSTATE_FAILED_SYNC: + if (!strcmp(text, "OK") || + (!strcmp(text, "> ") && link_ptr->old_status != GSMSTATE_CMGS_ARROW) || + !strcmp(text, "ERROR") || + !memcmp(text, "+CMS ERROR:", strlen("+CMS ERROR:"))) { + link_ptr->status = link_ptr->failed_sync_status; + link_ptr->non_read_count++; + } + break; + case GSMSTATE_ATE0_ATE0: + case GSMSTATE_ATE0_OK: + if (!strcmp(text, "ATE0")) { + link_ptr->status = GSMSTATE_ATE0_OK; + } else if (!strcmp(text, "OK")) { + /* + * ATE0 command complete. If a service centre address was + * specified for this link, initiate a CSCA command. + * Otherwise, initiate a CMGF command. + */ + if (strcmp(link_ptr->sim_pin, "")) { + link_ptr->status = GSMSTATE_CPIN; + } else if (strcmp(link_ptr->service_centre, "")) { + link_ptr->status = GSMSTATE_CSCA; + } else { + link_ptr->status = GSMSTATE_CMGF; + } + } else { + /* + * Unexpected data, so set status to failed_sync, which should + * continue reading until an OK/ERROR/+CMS ERROR occurs. + */ + if (strcmp(link_ptr->sim_pin, "")) { + link_ptr->failed_sync_status = GSMSTATE_CPIN; + } else if (strcmp(link_ptr->service_centre, "")) { + link_ptr->failed_sync_status = GSMSTATE_CSCA; + } else { + link_ptr->failed_sync_status = GSMSTATE_CMGF; + } + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + } + break; + case GSMSTATE_CPIN_OK: + if (!strcmp(text, "OK")) { + /* + * CPIN command complete. + */ + if (strcmp(link_ptr->service_centre, "")) { + link_ptr->status = GSMSTATE_CSCA; + } else { + link_ptr->status = GSMSTATE_CMGF; + } + } else { + if (strcmp(link_ptr->service_centre, "")) { + link_ptr->failed_sync_status = GSMSTATE_CSCA; + } else { + link_ptr->failed_sync_status = GSMSTATE_CMGF; + } + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + } + break; + case GSMSTATE_CSCA_OK: + if (!strcmp(text, "OK")) { + /* + * CSCA command complete. + */ + link_ptr->status = GSMSTATE_CMGF; + } else { + link_ptr->failed_sync_status = GSMSTATE_CMGF; + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + } + break; + case GSMSTATE_CMGF_OK: + if (!strcmp(text, "OK")) { + /* + * CMGF command complete. + */ + link_ptr->status = GSMSTATE_CNMI; + } else { + /* + * If the CMGF message failed, then it's likely the gsm modem + * hasn't found the network yet, so wait for 2 seconds, and then + * reattempt. + */ + Ns_Log(Notice, "generic_gsm_read_logic: Failed CMGF message, its likely the modem either doesn't have a SIM card, or the modem is still registering with the network."); + sleep(2); + link_ptr->failed_sync_status = GSMSTATE_CMGF; + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + } + break; + case GSMSTATE_CNMI_OK: + if (!strcmp(text, "OK")) { + /* + * CNMI command complete. + */ + link_ptr->status = GSMSTATE_IDLE; + } else { + link_ptr->failed_sync_status = GSMSTATE_IDLE; + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + } + break; + case GSMSTATE_CMGS_ARROW: + if (strcmp(text, "> ")) { + generic_gsm_fail_current_message(link_ptr); + link_ptr->failed_sync_status = GSMSTATE_IDLE; + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + } else { + link_ptr->status = GSMSTATE_CMGS_MESSAGE; + } + break; + case GSMSTATE_CMGS_MESSAGE: + /* + * We should never get any data whilst in this state, so its always + * an error. Some GSM modems (notably, the Wavecom 02) return an + * error indicator "ERROR" straight after the ">" if there is no + * SIM card (or the SIM card isn't initialised) in the modem. + */ + generic_gsm_fail_current_message(link_ptr); + link_ptr->failed_sync_status = GSMSTATE_IDLE; + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + break; + case GSMSTATE_CMGS_RESP: + if (!memcmp(text, "+CMGS:", strlen("+CMGS:"))) { + link_ptr->status = GSMSTATE_CMGS_OK; + } else { + generic_gsm_fail_current_message(link_ptr); + link_ptr->failed_sync_status = GSMSTATE_IDLE; + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + } + break; + case GSMSTATE_CMGS_OK: + if (!strcmp(text, "OK")) { + /* + * CMGS command complete. + */ + /* + * Sleep, as the generic_gsm can't seem to keep up (that, or + * the network. + */ + smsq_set_msg_status(link_ptr->msg_ptr->msg_id, + link_ptr->out_group_ptr, + Q_MSG_STATUS_COMPLETE); + link_ptr->msg_ptr = NULL; + usleep(link_ptr->post_send_delay_usecs); + link_ptr->status = GSMSTATE_IDLE; + link_ptr->non_read_count++; + } else { + generic_gsm_fail_current_message(link_ptr); + usleep(link_ptr->post_send_delay_usecs); + link_ptr->failed_sync_status = GSMSTATE_IDLE; + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + } + break; + case GSMSTATE_CMGL_DATA: + /* + * PDU mode receive + */ + if (link_ptr->use_pdu_p) { + if (link_ptr->cmgl_status == 1) { + /* + * We're expecting the message content here, so decode the pdu + * string, and submit the message onto the queue. + * Add the message number into the "message numbers to delete" + * hashtable. + */ + link_ptr->cmgl_status = 0; + generic_gsm_decode_pdu(text, + msisdn, + msg_data, + &msg_data_length, + &unpacked_size, + &data_coding_scheme, + &smsc_timestamp); + smsq_submit_8bit(link_ptr->in_group_ptr, + msg_id, + msisdn, NULL, + msg_data, + msg_data_length, + unpacked_size, + data_coding_scheme, + DCS_DEFAULT, + &smsc_timestamp, + NULL, NULL); + + entry = Tcl_CreateHashEntry(&link_ptr->cmgd_ids, + link_ptr->cmgl_msg_num, &new_flag); + Tcl_SetHashValue(entry, NULL); + } else if (!memcmp(text, "+CMGL:", strlen("+CMGL:"))) { + /* + * Lets extract the message number and msisdn. + */ + strcpy(cmgl_text, text); + cur_pos = cmgl_text; + cur_pos = strchr(cur_pos, ','); + if (cur_pos != NULL) { + *cur_pos = '\0'; + cmgl_msg_num = &cmgl_text[7]; + cur_pos = strchr(cur_pos+1, ','); + } + /* Skip the next two commas */ + if (cur_pos != NULL) { + cur_pos = strchr(cur_pos+1, ','); + } + if (cur_pos != NULL) { + link_ptr->pdu_length = (int)strtol(cur_pos+1, NULL, 10); + strcpy(link_ptr->cmgl_msg_num, cmgl_msg_num); + link_ptr->cmgl_status = 1; + } else { + Ns_Log(Error,"generic_gsm_link_reader: Unable to parse CMGL string \"%s\"", text); + } + } else if (!strcmp(text, "OK")) { + /* + * CMGL command complete. + */ + link_ptr->status = GSMSTATE_CMGD; + } else { + link_ptr->failed_sync_status = GSMSTATE_CMGD; + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + } + } else { + /* + * Text mode receive + */ + if (link_ptr->cmgl_status == 1) { +#ifdef CFG_DIGICOM_HACK + /************************************************************** + * Digicom wierdity fix. If the incoming message content is OK, + * either a blank message has been sent in (and this is the modems + * "OK" response), or the user has sent in OK. In the latter case, + * we'll probably get some unsolicited data from the modem. + * We also have to cope with an unexpected "+CMGL:" message, which + * might occur if there are more messages in this CMGL read series, + * and this message is a blank one. I.e: + * +CMGL: ...... + * +CMGL: ...... + * Content of the second message. + * OK + * + **************************************************************/ + if (!strcmp(text, "OK") || + !memcmp(text, "+CMGL:", strlen("+CMGL:"))) { + link_ptr->cmgl_status = 0; + smsq_submit_ascii(link_ptr->in_group_ptr, + msg_id, + link_ptr->cmgl_msisdn, link_ptr->msisdn, + "OK", + NULL, NULL, NULL); + entry = Tcl_CreateHashEntry(&link_ptr->cmgd_ids, + link_ptr->cmgl_msg_num, &new_flag); + Tcl_SetHashValue(entry, NULL); + + /* + * If the message content was OK, this was probably the last + * message in the CMGL series, so lets change states (this is a + * bit dodgy, as if + * we've received more than one message, and the first message was + * an explicit "OK" message, we'll be ignoring further messages. + * I believe these will get picked up the next time round). + * Otherwise, it was another CMGL: message, so lets process this + * text as though it was another message (by calling this function + * again with the same text). + */ + if (!strcmp(text, "OK")) { + link_ptr->status = GSMSTATE_CMGD; + } else { + /* + * + */ + generic_gsm_read_logic(link_ptr, text); + } + } else { +#endif + /* + * We're expecting the message content here, so submit the + * message and data to the queue. Add the message number into the + * "message numbers to delete" hashtable. + * + * NB: Currently not filling in timestamp for this message. + */ + link_ptr->cmgl_status = 0; + smsq_submit_ascii(link_ptr->in_group_ptr, + msg_id, + link_ptr->cmgl_msisdn, link_ptr->msisdn, + text, + NULL, NULL, NULL); + + entry = Tcl_CreateHashEntry(&link_ptr->cmgd_ids, + link_ptr->cmgl_msg_num, &new_flag); + Tcl_SetHashValue(entry, NULL); +#ifdef CFG_DIGICOM_HACK + } +#endif + } else if (!memcmp(text, "+CMGL:", strlen("+CMGL:"))) { + strcpy(cmgl_text, text); + cur_pos = cmgl_text; + /* + * Lets extract the message number and msisdn. + */ + cur_pos = strchr(cur_pos, ','); + if (cur_pos != NULL) { + *cur_pos = '\0'; + cmgl_msg_num = &cmgl_text[7]; + cur_pos = strchr(cur_pos+1, ','); + } + if (cur_pos != NULL) { + cmgl_msisdn = cur_pos + 2; + } + if (cur_pos != NULL) { + cur_pos = strchr(cmgl_msisdn, '"'); + *cur_pos = '\0'; + link_ptr->cmgl_status = 1; + strcpy(link_ptr->cmgl_msg_num, cmgl_msg_num); + strcpy(link_ptr->cmgl_msisdn, cmgl_msisdn); + } else { + Ns_Log(Error,"generic_gsm_link_reader: Unable to parse CMGL string \"%s\"", text); + } + } else if (!strcmp(text, "OK")) { + /* + * CMGL command complete. + */ + link_ptr->status = GSMSTATE_CMGD; + } else { + link_ptr->failed_sync_status = GSMSTATE_CMGD; + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + } + } + break; + + case GSMSTATE_CMGD_OK: + if (!strcmp(text, "OK")) { + /* + * CMGD command complete, schedule the next one. + */ + link_ptr->status = GSMSTATE_CMGD; + Tcl_DeleteHashEntry(link_ptr->cmgd_search_ptr); + } else { + link_ptr->failed_sync_status = GSMSTATE_CMGD; + link_ptr->status = GSMSTATE_FAILED_SYNC; + generic_gsm_read_logic(link_ptr, text); + } + break; + default: + if (!strncmp(text, "+CMTI:", 6)) { + link_ptr->status = GSMSTATE_CMGL; + } else { + Ns_Log(Error,"generic_gsm_link_reader: Unsolicited modem data \"%s\"", text); + } + break; + } +} + +void generic_gsm_fail_current_message(struct generic_gsm_link *link_ptr) { + link_ptr->payload_ptr->del_attempt_count++; + if (link_ptr->max_retries != -1 && + link_ptr->payload_ptr->del_attempt_count >= link_ptr->max_retries) { + Ns_Log(Error,"generic-gsm: Link %s: Message id %s failed after %d delivery attempt(s)", + link_ptr->link_id, + link_ptr->msg_ptr->msg_id, + link_ptr->payload_ptr->del_attempt_count); + smsq_set_msg_status(link_ptr->msg_ptr->msg_id, + link_ptr->out_group_ptr, + Q_MSG_STATUS_FAILED); + } else { + smsq_set_msg_status(link_ptr->msg_ptr->msg_id, + link_ptr->out_group_ptr, + Q_MSG_STATUS_READY); + } + link_ptr->msg_ptr = NULL; +} + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_link_event_handler -- + * + * Converts thread events into + * + * Results: + * + *---------------------------------------------------------------------- + */ +static void generic_gsm_link_event_handler(void *arg) +{ + struct generic_gsm_link *link_ptr; + struct q_group *group_ptr; + char link_id[20]; + Ns_Time waittime; + unsigned char msg_buf; + int finished; + int result; + SOCKET msg_pipe[2]; + + link_ptr = (struct generic_gsm_link *) arg; + + group_ptr = link_ptr->out_group_ptr; + strcpy(link_id, link_ptr->link_id); + msg_pipe[0] = link_ptr->msg_pipe[0]; + msg_pipe[1] = link_ptr->msg_pipe[1]; + + finished = 0; + while (!finished) { + Ns_GetTime(&waittime); + Ns_IncrTime(&waittime, 1, 0); // One second, beware changing this, as + // this will affect the device timeout + // algorithm. + result = Ns_CondTimedWait(&group_ptr->event, + &group_ptr->event_lock, + &waittime); + if (generic_gsm_get_link_ptr(link_id) != NULL) { + switch (result) { + case NS_OK: + if (link_ptr->link_status == GENERICGSM_ONLINE) { + msg_buf = GENERICGSM_GROUP_ACTIVITY; + blocked_send(msg_pipe[1], &msg_buf, 1, 0); + } + break; + case NS_TIMEOUT: + /* + * Timeout, so do nothing. + */ + break; + default: + Ns_Log(Error, + "generic_gsm_event_handler: Error from CondTimedWait: %d", + result); + break; + } + if (link_ptr->status != GSMSTATE_IDLE) { + if (link_ptr->sent_wait_count > link_ptr->device_timeout_secs) { + link_ptr->sent_wait_count = 0; + Ns_Log(Error, + "generic_gsm_event_handler: %d second response timeout, bouncing generic_gsm link %s", link_ptr->device_timeout_secs, link_ptr->link_id); + generic_gsm_set_status(link_ptr, GENERICGSM_OFFLINE); + generic_gsm_set_status(link_ptr, GENERICGSM_ONLINE); + } else { + link_ptr->sent_wait_count++; + } + } + } else { + /* + * Link has been deleted so set the finish flag. + */ + finished = 1; + } + } + sleep(1); + ns_sockclose(msg_pipe[0]); + ns_sockclose(msg_pipe[1]); + Ns_Log(Notice,"generic_gsm_link_event_handler: Exiting"); + Ns_ThreadExit (0); +} + +int blocked_recv(int sock, void *msg_buf, int msg_size, + int flags) +{ + int read_len; + int read_val; + read_len = 0; + read_val = 0; + + while (read_len < msg_size) { + if ((read_val = recv(sock, + msg_buf + read_len, + msg_size - read_len, flags)) <= 0) { + read_len = -1; + break; + } else { + read_len += read_val; + } + } +// Ns_Log(Notice, "(2) recv %d, %d, %d", sock, msg_size, *((char *) msg_buf)); + return read_len; +} + +int blocked_send(int sock, const void *msg_buf, int msg_size, int flags) +{ + int write_len; + int write_val; + write_len = 0; + write_val = 0; + +// Ns_Log(Notice, "send %d, %d, %d", sock, msg_size, *((char *) msg_buf)); + while (write_len < msg_size) { + if ((write_val = send(sock, + msg_buf + write_len, + msg_size - write_len,flags)) <= 0) { + write_len = -1; + break; + } else { + write_len += write_val; + } + } + + return write_len; +} + +int blocked_read(int fd, void *msg_buf, int msg_size) +{ + int read_len; + int read_val; + read_len = 0; + read_val = 0; + + while (read_len < msg_size) { + if ((read_val = read(fd, + msg_buf + read_len, + msg_size - read_len)) <= 0) { + read_len = -1; + break; + } else { + read_len += read_val; + } + } + return read_len; +} + +int blocked_write(int fd, const void *msg_buf, int msg_size) +{ + int write_len; + int write_val; + write_len = 0; + write_val = 0; + + Ns_Log(Notice, "Blocked write to %d, of %d bytes: %s", fd, msg_size, msg_buf); + while (write_len < msg_size) { + if ((write_val = write(fd, + msg_buf + write_len, + msg_size - write_len)) <= 0) { + write_len = -1; + break; + } else { + write_len += write_val; + } + } + + return write_len; +} + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_get_link_ptr -- + * + * Returns the pointer to the link structure (lookup in the hash table) + * specified via the link_id. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static struct generic_gsm_link * +generic_gsm_get_link_ptr(char *link_id) +{ + Tcl_HashEntry *entry; + + Ns_RWLockRdLock(&g_generic_gsm_links_mutex); + entry = Tcl_FindHashEntry(&g_generic_gsm_links, link_id); + if (entry == NULL) { + Ns_RWLockUnlock(&g_generic_gsm_links_mutex); + return NULL; + } + Ns_RWLockUnlock(&g_generic_gsm_links_mutex); + + return Tcl_GetHashValue(entry); +} + +static int +generic_gsm_delete(struct generic_gsm_link *link_ptr) { + unsigned char msg_buf; + msg_buf = GENERICGSM_DELETE; + blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); + return NS_TRUE; +} + +static int +generic_gsm_set_status(struct generic_gsm_link *link_ptr, + int status) { + unsigned char msg_buf; + msg_buf = (char) status; + blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); + return NS_TRUE; +} + +static int +generic_gsm_update(struct generic_gsm_link *link_ptr, + char *device, + int device_timeout_secs, + char *service_centre, + char *sim_pin, + int poll_time_secs, + int post_send_delay_usecs, + int use_pdu_p, + int max_retries, + char *msisdn) { + unsigned char msg_buf; + + strcpy(link_ptr->device, device); + link_ptr->device_timeout_secs = device_timeout_secs; + strcpy(link_ptr->service_centre, service_centre); + strcpy(link_ptr->sim_pin, sim_pin); + link_ptr->poll_time_secs = poll_time_secs; + link_ptr->post_send_delay_usecs = post_send_delay_usecs; + link_ptr->use_pdu_p = use_pdu_p; + link_ptr->max_retries = max_retries; + strcpy(link_ptr->msisdn, msisdn); + + msg_buf = GENERICGSM_UPDATE; + blocked_send(link_ptr->msg_pipe[1], &msg_buf, 1, 0); + return NS_TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_add_commands -- + * + * Adds the generic_gsm API functions to the Tcl interpreter. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +generic_gsm_add_commands(Tcl_Interp *interp, ClientData data) { + Tcl_CreateCommand(interp, "generic_gsm_init", + generic_gsm_tcl_init, + NULL, NULL); + Tcl_CreateCommand(interp, "generic_gsm_delete", + generic_gsm_tcl_delete, + NULL, NULL); + Tcl_CreateCommand(interp, "generic_gsm_set_status", + generic_gsm_tcl_set_status, + NULL, NULL); + Tcl_CreateCommand(interp, "generic_gsm_update", + generic_gsm_tcl_update, + NULL, NULL); + return NS_OK; +} + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_tcl_init -- + * + * Tcl API function to initialise a new/starting link. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +generic_gsm_tcl_init(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + struct q_group *in_group_ptr; + struct q_group *out_group_ptr; + char *link_id; + char *device; + char *service_centre; + char *sim_pin; + char *status_ptr; + char *msisdn; + int status; + int device_timeout_secs; + int poll_time_secs; + int post_send_delay_usecs; + int use_pdu_p; + int max_retries; + + if (argc < 13 || argc > 14) { + Tcl_AppendResult(interp, "Usage: ", argv[0], + " link_id in_group out_group status device device_timeout_secs service_centre sim_pin poll_time_secs post_send_delay_usecs use_pdu_p max_retries [msisdn]", NULL); + return TCL_ERROR; + } + + link_id = argv[1]; + + in_group_ptr = q_get_group_ptr(g_server, argv[2]); + if (in_group_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); + return TCL_ERROR; + } + + out_group_ptr = q_get_group_ptr(g_server, argv[3]); + if (out_group_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); + return TCL_ERROR; + } + + status_ptr = argv[4]; + if (!strcmp(status_ptr,"online")) { + status = GENERICGSM_ONLINE; + } else if (!strcmp(status_ptr,"offline")) { + status = GENERICGSM_OFFLINE; + } else { + Tcl_AppendResult(interp, "Unknown status : ", status_ptr, + ", expecting online|offline", NULL); + return TCL_ERROR; + } + + device = argv[5]; + + if (Tcl_GetInt(interp, argv[6], &device_timeout_secs) != TCL_OK) { + return TCL_ERROR; + } + + service_centre = argv[7]; + sim_pin = argv[8]; + + if (Tcl_GetInt(interp, argv[9], &poll_time_secs) != TCL_OK) { + return TCL_ERROR; + } + + if (Tcl_GetInt(interp, argv[10], &post_send_delay_usecs) != TCL_OK) { + return TCL_ERROR; + } + + if (Tcl_GetInt(interp, argv[11], &use_pdu_p) != TCL_OK) { + return TCL_ERROR; + } + + if (Tcl_GetInt(interp, argv[12], &max_retries) != TCL_OK) { + return TCL_ERROR; + } + + if (argc == 14) { + msisdn = argv[13]; + } else { + msisdn = ""; + } + + generic_gsm_init(link_id, + in_group_ptr, + out_group_ptr, + status, + device, + device_timeout_secs, + service_centre, + sim_pin, + poll_time_secs, + post_send_delay_usecs, + use_pdu_p, + max_retries, + msisdn); + + Tcl_AppendResult(interp, "1", NULL); + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_tcl_delete -- + * + * Tcl API function to delete a running link. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +generic_gsm_tcl_delete(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + struct generic_gsm_link *link_ptr; + char *link_id; + + if (argc != 2) { + Tcl_AppendResult(interp, "Usage: ", argv[0], + " link_id", NULL); + return TCL_ERROR; + } + + link_id = argv[1]; + link_ptr = generic_gsm_get_link_ptr(link_id); + + if (link_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown link_id: ", link_id, NULL); + return TCL_ERROR; + } + + generic_gsm_delete(link_ptr); + Tcl_AppendResult(interp, "1", NULL); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_tcl_connect_p -- + * + * Tcl API function to check whether a link is connected + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +generic_gsm_tcl_connect_p(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + struct generic_gsm_link *link_ptr; + char *link_id; + + if (argc != 2) { + Tcl_AppendResult(interp, "Usage: ", argv[0], + " link_id", NULL); + return TCL_ERROR; + } + + link_id = argv[1]; + link_ptr = generic_gsm_get_link_ptr(link_id); + + if (link_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown link_id: ", link_id, NULL); + return TCL_ERROR; + } + + if (link_ptr->sock != -1) { + Tcl_AppendResult(interp, "1", NULL); + return TCL_OK; + } else { + Tcl_AppendResult(interp, "0", NULL); + return TCL_OK; + } +} + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_tcl_set_status -- + * + * Tcl API function to set the status of a running link. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +generic_gsm_tcl_set_status(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + struct generic_gsm_link *link_ptr; + char *link_id; + char *status_ptr; + int status; + + if (argc != 3) { + Tcl_AppendResult(interp, "Usage: ", argv[0], + " link_id online|offline", NULL); + return TCL_ERROR; + } + + link_id = argv[1]; + link_ptr = generic_gsm_get_link_ptr(link_id); + + if (link_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown link_id: ", link_id, NULL); + return TCL_ERROR; + } + + status_ptr = argv[2]; + if (!strcmp(status_ptr,"online")) { + status = GENERICGSM_ONLINE; + } else if (!strcmp(status_ptr,"offline")) { + status = GENERICGSM_OFFLINE; + } else { + Tcl_AppendResult(interp, "Unknown status : ", status_ptr, + ", expecting online|offline", NULL); + return TCL_ERROR; + } + + generic_gsm_set_status(link_ptr, status); + + Tcl_AppendResult(interp, "1", NULL); + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_tcl_update -- + * + * Tcl API function to update parameters for a running link. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +generic_gsm_tcl_update(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + struct generic_gsm_link *link_ptr; + char *link_id; + char *device; + char *service_centre; + char *sim_pin; + char *msisdn; + int device_timeout_secs; + int poll_time_secs; + int post_send_delay_usecs; + int use_pdu_p; + int max_retries; + + if (argc < 10 || argc > 11) { + Tcl_AppendResult(interp, "Usage: ", argv[0], + " link_id device device_timeout_secs service_centre sim_pin poll_time_secs post_send_delay_usecs use_pdu_p max_retries [msisdn]", NULL); + return TCL_ERROR; + } + + link_id = argv[1]; + link_ptr = generic_gsm_get_link_ptr(link_id); + + if (link_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown link_id: ", link_id, NULL); + return TCL_ERROR; + } + + device = argv[2]; + + if (Tcl_GetInt(interp, argv[3], &device_timeout_secs) != TCL_OK) { + return TCL_ERROR; + } + + service_centre = argv[4]; + sim_pin = argv[5]; + + if (Tcl_GetInt(interp, argv[6], &poll_time_secs) != TCL_OK) { + return TCL_ERROR; + } + + if (Tcl_GetInt(interp, argv[7], &post_send_delay_usecs) != TCL_OK) { + return TCL_ERROR; + } + + if (Tcl_GetInt(interp, argv[8], &use_pdu_p) != TCL_OK) { + return TCL_ERROR; + } + + if (Tcl_GetInt(interp, argv[9], &max_retries) != TCL_OK) { + return TCL_ERROR; + } + + if (argc == 11) { + msisdn = argv[10]; + } else { + msisdn = ""; + } + + generic_gsm_update(link_ptr, + device, + device_timeout_secs, + service_centre, + sim_pin, + poll_time_secs, + post_send_delay_usecs, + use_pdu_p, + max_retries, + msisdn); + Tcl_AppendResult(interp, "1", NULL); + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * generic_gsm_encode_pdu -- + * + * Function to encode the pdu string for transmission of an SMS whilst + * the GSM modem is in PDU mode. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static void generic_gsm_encode_pdu(unsigned char *pdu_string_ptr, + int *pdu_length_ptr, + struct generic_gsm_link *link_ptr) { + char tmp_buf[256]; + char *msisdn_ptr; + int international_p; + int i; + int pdu_length; + + pdu_string_ptr[0] = '\0'; + pdu_length = 0; + + /* + * Calculate the service centre number length in bytes (excluding the + * "+", which gets removed, and including the number format byte). + * Append the byteswapped service centre number, remembering to skip + * the preceeding "+". + */ + sprintf(tmp_buf, "%.2X91", ((strlen(link_ptr->service_centre) - 1) / 2) + 1); + strcat(pdu_string_ptr, tmp_buf); + + generic_gsm_byteswap(&link_ptr->service_centre[1], tmp_buf); + strcat(pdu_string_ptr, tmp_buf); + + /* + * TP flags + */ + sprintf(tmp_buf, "%.2X", link_ptr->payload_ptr->tp_mask); + strcat(pdu_string_ptr, tmp_buf); + pdu_length++; + + /* + * Message reference + */ + strcat(pdu_string_ptr, "00"); + pdu_length++; + + /* + * Add the MSISDN. + */ + if (link_ptr->payload_ptr->dest_address[0] == '+') { + international_p = 1; + msisdn_ptr = &link_ptr->payload_ptr->dest_address[1]; + } else if (link_ptr->payload_ptr->dest_address[0] == '0') { + international_p = 0; + msisdn_ptr = &link_ptr->payload_ptr->dest_address[1]; + } else { + international_p = 1; + msisdn_ptr = &link_ptr->payload_ptr->dest_address[0]; + } + sprintf(tmp_buf, "%.2X", strlen(msisdn_ptr)); + strcat(pdu_string_ptr, tmp_buf); + pdu_length++; + + if (international_p) { + strcat(pdu_string_ptr, "91"); + } else { + strcat(pdu_string_ptr, "81"); + } + pdu_length++; + + generic_gsm_byteswap(msisdn_ptr, tmp_buf); + strcat(pdu_string_ptr, tmp_buf); + pdu_length += strlen(tmp_buf) / 2; + + /* + * Protocol id. + */ + sprintf(tmp_buf, "%.2X", link_ptr->payload_ptr->protocol_id); + strcat(pdu_string_ptr, tmp_buf); + pdu_length++; + + /* + * Data coding scheme + */ + sprintf(tmp_buf, "%.2X", link_ptr->payload_ptr->data_coding_scheme); + strcat(pdu_string_ptr, tmp_buf); + pdu_length++; + + /* + * Validity period. + */ + if (link_ptr->payload_ptr->tp_mask & (TP_VAL_PER_REL | TP_VAL_PER_ABS)) { + sprintf(tmp_buf, "%.2X", (int)link_ptr->payload_ptr->validity_period); + strcat(pdu_string_ptr, tmp_buf); + pdu_length++; + } + + sprintf(tmp_buf, "%.2X", link_ptr->payload_ptr->unpacked_data_len); + strcat(pdu_string_ptr, tmp_buf); + pdu_length++; + + for (i = 0; i < link_ptr->payload_ptr->user_data_len; i++) { + sprintf(tmp_buf, "%.2X", link_ptr->payload_ptr->user_data[i]); + strcat(pdu_string_ptr, tmp_buf); + } + pdu_length += link_ptr->payload_ptr->user_data_len; + + *pdu_length_ptr = pdu_length; +} + +static void generic_gsm_decode_pdu(unsigned char *pdu_string_ptr, + char *msisdn_ptr, + unsigned char *msg_data_ptr, + int *msg_data_size_ptr, + int *unpacked_size_ptr, + int *data_coding_scheme_ptr, + struct smsc_tm *smsc_timestamp_ptr) +{ + int smsc_len; + int msisdn_len; + int type_of_address; + int msisdn_char_num; + int data_coding_scheme; + int i; + unsigned char *cur_ptr; + unsigned char tmp_buf[3]; + unsigned char msisdn[128]; + unsigned char unswapped_timestamp[16]; + unsigned char timestamp[16]; + unsigned int gmt_byte; + + tmp_buf[2] = '\0'; + cur_ptr = pdu_string_ptr; +// 0791449737019037040C914477578790050000108092110462000441E19008 + + memcpy(tmp_buf, cur_ptr, 2); + smsc_len = strtol(tmp_buf,NULL,16); + + // Skip the length octet, the smsc address, and the inital 04 DELIVER byte + cur_ptr += smsc_len * 2 + 4; + + memcpy(tmp_buf, cur_ptr, 2); + msisdn_len = strtol(tmp_buf,NULL,16); + + // Skip the msisdn length octet + cur_ptr += 2; + + memcpy(tmp_buf, cur_ptr, 2); + type_of_address = strtol(tmp_buf,NULL,16); + + // Skip the type of address octet + cur_ptr += 2; + + if (msisdn_len % 2 == 1) { + msisdn_char_num = msisdn_len + 1; + } else { + msisdn_char_num = msisdn_len; + } + memcpy(msisdn, cur_ptr, msisdn_char_num); + msisdn[msisdn_char_num] = '\0'; + + if (type_of_address == 0x91) { + msisdn_ptr[0] = '+'; + generic_gsm_byteswap(msisdn, msisdn_ptr + 1); + msisdn_ptr[msisdn_len+1] = '\0'; + } else { + generic_gsm_byteswap(msisdn, msisdn_ptr); + msisdn_ptr[msisdn_len] = '\0'; + } + + // Skip the msisdn + cur_ptr += msisdn_char_num; + + // Skip the protocol id + cur_ptr += 2; + + memcpy(tmp_buf, cur_ptr, 2); + data_coding_scheme = strtol(tmp_buf,NULL,16); + + // Skip the data coding scheme + cur_ptr += 2; + + /* + * Extract the timestamp. + */ + memcpy(unswapped_timestamp, cur_ptr, 14); + unswapped_timestamp[14] = '\0'; + generic_gsm_byteswap(unswapped_timestamp, timestamp); + // year + memcpy(tmp_buf, timestamp, 2); + smsc_timestamp_ptr->tm_year = strtol(tmp_buf,NULL,10) + 2000; + // month + memcpy(tmp_buf, timestamp + 2, 2); + smsc_timestamp_ptr->tm_mon = strtol(tmp_buf,NULL,10) - 1; + // day + memcpy(tmp_buf, timestamp + 4, 2); + smsc_timestamp_ptr->tm_mday = strtol(tmp_buf,NULL,10); + // hour + memcpy(tmp_buf, timestamp + 6, 2); + smsc_timestamp_ptr->tm_hour = strtol(tmp_buf,NULL,10); + // minute + memcpy(tmp_buf, timestamp + 8, 2); + smsc_timestamp_ptr->tm_min = strtol(tmp_buf,NULL,10); + // sec + memcpy(tmp_buf, timestamp + 10, 2); + smsc_timestamp_ptr->tm_sec = strtol(tmp_buf,NULL,10); + // timezone (difference from GMT */ + memcpy(tmp_buf, timestamp + 12, 2); + gmt_byte = strtol(tmp_buf, NULL, 10); + if (gmt_byte & 128) { + smsc_timestamp_ptr->tm_tz = -(gmt_byte & 127); + } else { + smsc_timestamp_ptr->tm_tz = gmt_byte & 127; + } + cur_ptr += 14; + + memcpy(tmp_buf, cur_ptr, 2); + *unpacked_size_ptr = strtol(tmp_buf,NULL,16); + + cur_ptr += 2; + *msg_data_size_ptr = strlen(cur_ptr) / 2; + for (i = 0; i < (*msg_data_size_ptr); i++) { + memcpy(tmp_buf, cur_ptr, 2); + msg_data_ptr[i] = (unsigned char) strtol(tmp_buf,NULL,16); + cur_ptr += 2; + } +} + +static void generic_gsm_byteswap(char *in_string_ptr, + char *out_string_ptr) { + int len; + int i; + + len = strlen(in_string_ptr); + + for (i = 0; i < len; i += 2) { + out_string_ptr[i + 1] = in_string_ptr[i]; + if ((i + 1) < len) { + out_string_ptr[i] = in_string_ptr[i + 1]; + } + } + out_string_ptr[i] = '\0'; + out_string_ptr[i+1] = '\0'; + + if ((len % 2) == 1) { + out_string_ptr[i] = 'F'; + } +} Index: openacs-4/contrib/misc/smsc/generic-gsm/generic-gsm.h =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/generic-gsm/generic-gsm.h,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/generic-gsm/generic-gsm.h 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,171 @@ +/* + * This file is part of Generic-gsm. + * + * Generic-gsm is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Generic-gsm is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Generic-gsm; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * No rights reserved. This code is public domain. + * + * Peter Harper + * + * $Id: generic-gsm.h,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +#include +#include +#include + +#ifndef _GENERICGSM_POCKET_H +#define _GENERICGSM_POCKET_H + +/* + * Define inter-pipe messages. Note that any messages with a value of + * > 100, are considered state-changes (see below). + */ +#define GENERICGSM_ONLINE 0 +#define GENERICGSM_OFFLINE 1 +#define GENERICGSM_DELETE 2 +#define GENERICGSM_UPDATE 3 +#define GENERICGSM_CONNECTED 10 +#define GENERICGSM_DISCONNECTED 11 +#define GENERICGSM_GROUP_ACTIVITY 12 + +/* + * Define message states. + * Currently all the state change is hardcoded in switches and code logic. + * This would be better done by defining a data-driven state machine. + */ + +/* idle */ +#define GSMSTATE_IDLE 100 +/* ate0 */ +#define GSMSTATE_ATE0 105 +#define GSMSTATE_ATE0_ATE0 106 +#define GSMSTATE_ATE0_OK 107 +/* cpin */ +#define GSMSTATE_CPIN 108 +#define GSMSTATE_CPIN_OK 109 +/* csca */ +#define GSMSTATE_CSCA 110 +#define GSMSTATE_CSCA_OK 111 +/* cmgf */ +#define GSMSTATE_CMGF 115 +#define GSMSTATE_CMGF_OK 116 +/* scrt */ +#define GSMSTATE_CNMI 120 +#define GSMSTATE_CNMI_OK 121 +/* cmgs */ +#define GSMSTATE_CMGS 125 +#define GSMSTATE_CMGS_ARROW 126 +#define GSMSTATE_CMGS_MESSAGE 127 +#define GSMSTATE_CMGS_RESP 128 +#define GSMSTATE_CMGS_OK 129 +/* cmgl */ +#define GSMSTATE_CMGL 130 +#define GSMSTATE_CMGL_DATA 131 +/* cmgd */ +#define GSMSTATE_CMGD 135 +#define GSMSTATE_CMGD_OK 136 + +#define GSMSTATE_FAILED_SYNC 200 + +#define GENERICGSM_MAX_BEFORE_READ 5 + +// #define Version "1.1" + +/* + * Generic Gsm link datastructure + */ +struct generic_gsm_link { + char link_id[20]; + char device[1024]; + int device_timeout_secs; + char service_centre[1024]; + char msisdn[1024]; + char sim_pin[1024]; + int poll_time_secs; + int post_send_delay_usecs; + unsigned char old_status; + unsigned char status; + unsigned char link_status; + struct q_group *in_group_ptr; + struct q_group *out_group_ptr; + SOCKET msg_pipe[2]; + int sock; + struct termios tty; + int non_read_count; + struct q_message *msg_ptr; + struct smsq_sms_submit *payload_ptr; + int failed_sync_status; + int cmgl_status; + char cmgl_text[1024]; + char cmgl_msg_num[128]; + char cmgl_msisdn[128]; + Tcl_HashTable cmgd_ids; + Tcl_HashEntry *cmgd_search_ptr; + int sent_wait_count; + int use_pdu_p; + unsigned char pdu_string[1024]; + int pdu_length; + int max_retries; +}; + +/* + * Function prototypes. + */ +static int generic_gsm_init(char *link_id, + struct q_group *in_group_ptr, + struct q_group *out_group_ptr, + int status, + char *device, + int device_timeout_secs, + char *service_centre, + char *sim_pin, + int poll_time_secs, + int post_send_delay_usecs, + int use_pdu_p, + int max_retries, + char *msisdn); +static int generic_gsm_delete(struct generic_gsm_link *link_ptr); +static int generic_gsm_set_status(struct generic_gsm_link *link_ptr, + int status); +static int generic_gsm_update(struct generic_gsm_link *link_ptr, + char *device, + int device_timeout_secs, + char *service_centre, + char *sim_pin, + int poll_time_secs, + int post_send_delay_usecs, + int use_pdu_p, + int max_retries, + char *msisdn); +static int generic_gsm_link_execute(struct generic_gsm_link *link_ptr); +static void generic_gsm_link_reader(void *arg); +static void generic_gsm_link_event_handler(void *arg); + +static void generic_gsm_encode_pdu(unsigned char *pdu_string_ptr, + int *pdu_length, + struct generic_gsm_link *link_ptr); +static void generic_gsm_decode_pdu(unsigned char *pdu_string_ptr, + char *msisdn_ptr, + unsigned char *msg_data_ptr, + int *msg_data_size_ptr, + int *unpacked_size_ptr, + int *data_coding_scheme, + struct smsc_tm *smsc_timestamp_ptr); + +static void generic_gsm_byteswap(char *in_string_ptr, + char *out_string_ptr); + + +#endif Index: openacs-4/contrib/misc/smsc/generic-gsm/gpl.txt =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/generic-gsm/gpl.txt,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/generic-gsm/gpl.txt 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. Index: openacs-4/contrib/misc/smsc/qcluster/Makefile =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/Makefile,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/qcluster/Makefile 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,77 @@ +# @(#) $Header: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/Makefile,v 1.1 2003/12/02 06:17:38 rmello Exp $ +# + +ifdef INST +NSHOME ?= $(INST) +else +NSHOME ?= ../aolserver +endif + +module = qcluster +cvspath = nsd-modules/$(module) +version_ = $(subst .,_,$(version)) +distdir = $(module)-$(version) +distfile = $(distdir).tar.gz + +MOD = qcluster.so + +# +# Set the objects to build +# + +OBJS = cluster.o locks.o clustercomms.o message.o qcluster.o + +include $(NSHOME)/include/Makefile.global + +all: $(MOD) + +%.o: %.c + $(CC) -c $(CFLAGS) -D_TCL82 $< -o $@ + +$(MOD): $(OBJS) + $(RM) $@ + $(LDSO) $(LDSOFLAGS) -o $@ $^ $(MODLIBS) + +install: all + $(RM) $(INSTBIN)/$(MOD) + $(CP) $(MOD) $(INSTBIN) + +clean: + $(RM) $(MOD) $(OBJS) + +clobber: clean + $(RM) *.so *.o *.a *~ + +distclean: clobber + $(RM) TAGS tags core *.gz + +release: check-version-var + cvs rtag -r stable release-$(version_) $(cvspath) + +force-release: check-version-var + cvs rtag -F -r stable release-$(version_) $(cvspath) + +dist: check-version-var $(distfile) + +publish: dist + scp "$(distfile)" open-msg.com:www/aolserver + ssh open-msg.com 'cd www/aolserver/src && rm -rf "./$(module)-"* && tar xvzf "../$(distfile)"' + ssh -t open-msg.com vi www/aolserver/index.html + +$(distfile): + rm -rf work + mkdir work + cd work && cvs -Q export -r "release-$(version_)" \ + -d "$(distdir)" "$(cvspath)" + find work -type f | xargs perl -pi -e 's/\@VER\@/$(version)/g' + ( cd work && tar cvf - "$(distdir)" ) | gzip -9 > "$(distfile)" + rm -rf work + +.PHONY: check-version-var + +check-version-var: + @if [ "$(version)" = "" ]; then \ + echo 1>&2 "\$$version must be set to version number."; \ + exit 1; \ + fi + Index: openacs-4/contrib/misc/smsc/qcluster/cluster.c =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/cluster.c,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/qcluster/cluster.c 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,400 @@ +/* + * This file is part of QCluster. + * + * QCluster is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * QCluster is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QCluster; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: cluster.c,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +static char rcsid[] = "@(#) $Id: "; + +#include "ns.h" +#include "cluster.h" +#include "clustercomms.h" +#include "message.h" +#include "locks.h" + + +/* + * Static procedures. + */ +int +q_data_get_ll_entry(struct q_group *group_ptr, + char *msg_id, + struct q_message_entry **queue_entry_ptr, + struct q_message_entry **queue_by_stat_entry_ptr); + +/* + * Global qcluster data + */ +Ns_RWLock g_cluster_mutex; +Tcl_HashTable g_cluster; +struct q_server *g_cluster_order[Q_MAX_SERVERS]; +int g_cluster_size; +int g_this_server_pos; + +char *g_addr; +char *g_iam; +struct q_server *g_server; +int g_port; +int g_qcluster_started = 0; +Ns_Mutex g_recover_mutex; + +/* + * Macros to add and delete a message to/from a linked list + */ +#define Q_DATA_LIST_APPEND(msg_ptr, list_ptr) \ + { \ + struct q_message_entry *entry_ptr; \ + entry_ptr = (struct q_message_entry *) \ + Ns_Malloc(sizeof(struct q_message_entry)); \ + entry_ptr->msg_ptr = msg_ptr; \ + if (*list_ptr == NULL) { \ + *list_ptr = entry_ptr; \ + entry_ptr->prev_ptr = NULL; \ + entry_ptr->next_ptr = NULL; \ + } else { \ + struct q_message_entry *search_ptr; \ + search_ptr = *list_ptr; \ + while (search_ptr->next_ptr != NULL) { \ + search_ptr = search_ptr->next_ptr; \ + } \ + entry_ptr->next_ptr = NULL; \ + entry_ptr->prev_ptr = search_ptr; \ + search_ptr->next_ptr = entry_ptr; \ + } \ + } + +#define Q_DATA_LIST_DELETE(entry_ptr, list_ptr) \ + { \ + if (entry_ptr->prev_ptr == entry_ptr->next_ptr && \ + entry_ptr->next_ptr == NULL) { \ + *list_ptr = NULL; \ + } else { \ + if (entry_ptr->prev_ptr != NULL) { \ + entry_ptr->prev_ptr->next_ptr = entry_ptr->next_ptr; \ + } \ + if (entry_ptr->next_ptr != NULL) { \ + entry_ptr->next_ptr->prev_ptr = entry_ptr->prev_ptr; \ + } \ + if (*list_ptr == entry_ptr) { \ + *list_ptr = entry_ptr->next_ptr; \ + } \ + } \ + ns_free(entry_ptr); \ + } + +/**************************************************************************** + * The following section contains functions for manipulating message + * queues within groups. These are meant as a complete API between + * manipulating functions and the underlying data used to model the + * message queues. + * + * The original code used a hash table to model the messages, but this was + * changed to include a linked list in addition to this, so that an explicit + * ordering (based on time) could be used. + * + * NB: All these functions assume that locks on the specified group have + * *ALREADY BEEN ASSIGNED*. No explicit locking is done in these + * functions. + ****************************************************************************/ + +/* + *---------------------------------------------------------------------- + * + * q_data_add_msg -- + * + * Adds the specified message to a group + * + * Results: + * + * Side effects: + * New EvalThread will be created. + * + *---------------------------------------------------------------------- + */ + +int +q_data_add_msg(struct q_group *group_ptr, + struct q_message *msg_ptr) +{ + Tcl_HashEntry *entry_ptr; + int new_flag; +// Ns_Log(Notice,"q_data_add_msg: entry (%s)", msg_ptr->msg_id); + /* + * Create the linked list entry + */ + Q_DATA_LIST_APPEND(msg_ptr, (&group_ptr->queue_ll)); + Q_DATA_LIST_APPEND(msg_ptr, (&group_ptr->queue_by_stat_ll[msg_ptr->status])); +// entry_ptr = Tcl_CreateHashEntry(&group_ptr->queue_ht, +// msg_id, &new_flag); +// Tcl_SetHashValue(entry_ptr, (void *)msg_ptr); +// Ns_Log(Notice,"q_data_add_msg: exit (%s)", msg_ptr->msg_id); + return NS_TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * q_data_delete_msg -- + * + * Deletes the specified message from the group + * + * Results: + * + *---------------------------------------------------------------------- + */ +int +q_data_delete_msg(struct q_group *group_ptr, + char *msg_id, + int free_msg_ptr_p, + int free_payload_ptr_p) { + struct q_message_entry *main_entry_ptr; + struct q_message_entry *by_stat_entry_ptr; +// Ns_Log(Notice,"q_data_delete_msg: entry (%s)", msg_id); + if (q_data_get_ll_entry(group_ptr, msg_id, + &main_entry_ptr, &by_stat_entry_ptr) == NS_FALSE) { + Ns_Log(Error, "q_data_delete_msg: Message id %s in group %s not found", + msg_id, + group_ptr->grp_name); +// Ns_Log(Notice,"q_data_delete_msg: exit NS_FALSE (%s)", msg_id); + return NS_FALSE; + } else { + if (free_payload_ptr_p == NS_TRUE) { + ns_free(main_entry_ptr->msg_ptr->msg_ptr); + } + if (free_msg_ptr_p == NS_TRUE) { + ns_free(main_entry_ptr->msg_ptr); + } + Q_DATA_LIST_DELETE(main_entry_ptr, &group_ptr->queue_ll); + Q_DATA_LIST_DELETE(\ + by_stat_entry_ptr, + (&group_ptr->queue_by_stat_ll[by_stat_entry_ptr->msg_ptr->status])); +// Ns_Log(Notice,"q_data_delete_msg: exit NS_TRUE (%s)", msg_id); + return NS_TRUE; + } +} + + +/* + *---------------------------------------------------------------------- + * + * q_data_get_ll_entry -- + * + * Searches for entries in each linked list, given a message id. + * + * Results: + * NS_TRUE if entries in both linked lists were found. + * NS_FALSE if entries in both linked lists were not found. + * + *---------------------------------------------------------------------- + */ +int +q_data_get_ll_entry(struct q_group *group_ptr, + char *msg_id, + struct q_message_entry **queue_entry_ptr, + struct q_message_entry **queue_by_stat_entry_ptr) { + struct q_message_entry *entry_ptr; + struct q_message_entry *found_ptr; + + /* + * Search for an entry in the main message linked list. + */ + entry_ptr = group_ptr->queue_ll; + found_ptr = NULL; + while (entry_ptr != NULL && + found_ptr == NULL) { + if (!strcmp(entry_ptr->msg_ptr->msg_id, + msg_id)) { + found_ptr = entry_ptr; + } + entry_ptr = entry_ptr->next_ptr; + } + *queue_entry_ptr = found_ptr; + + /* + * Search for an entry in the status specific message linked list. + */ + if (found_ptr != NULL) { + entry_ptr = group_ptr->queue_by_stat_ll[found_ptr->msg_ptr->status]; + found_ptr = NULL; + while (entry_ptr != NULL && + found_ptr == NULL) { + if (!strcmp(entry_ptr->msg_ptr->msg_id, + msg_id)) { + found_ptr = entry_ptr; + } + entry_ptr = entry_ptr->next_ptr; + } + } + *queue_by_stat_entry_ptr = found_ptr; + + /* + * If either queue entry wasn't found, return NS_FALSE. Otherwise return + * NS_TRUE; + */ + if (queue_entry_ptr == NULL || + queue_by_stat_entry_ptr == NULL) { + return NS_FALSE; + } else { + return NS_TRUE; + } +} + +/* + *---------------------------------------------------------------------- + * + * q_data_get_msg_entry -- + * + * Searches for a message entry matching the specified message id. + * + * Results: + * NS_TRUE if entries in both linked lists were found. + * NS_FALSE if entries in both linked lists were not found. + * + *---------------------------------------------------------------------- + */ +struct q_message * +q_data_get_msg_entry(struct q_group *group_ptr, + char *msg_id) { + struct q_message_entry *entry_ptr; + struct q_message_entry *found_ptr; + + /* + * Search for an entry in the main message linked list. + */ + entry_ptr = group_ptr->queue_ll; + found_ptr = NULL; + while (entry_ptr != NULL && + found_ptr == NULL) { +//Ns_Log(Notice,"Entry %x -> %x", entry_ptr, entry_ptr->next_ptr); + if (!strcmp(entry_ptr->msg_ptr->msg_id, + msg_id)) { + found_ptr = entry_ptr; + } + entry_ptr = entry_ptr->next_ptr; + } + if (found_ptr == NULL) { + return NULL; + } else { + return found_ptr->msg_ptr; + } +} + + +/* + *---------------------------------------------------------------------- + * + * q_data_update_msg_status -- + * + * This function should be called when the status of a message + * structure is changed. This function moves the message to the + * appropriate status specific linked list. + * + * Results: + * NS_TRUE if entries in both linked lists were found. + * NS_FALSE if entries in both linked lists were not found. + * + *---------------------------------------------------------------------- + */ +int +q_data_update_msg_status(struct q_group *group_ptr, + struct q_message *msg_ptr, + int new_status) { + struct q_message_entry *main_entry_ptr; + struct q_message_entry *by_stat_entry_ptr; + + if (q_data_get_ll_entry(group_ptr, msg_ptr->msg_id, + &main_entry_ptr, &by_stat_entry_ptr) == NS_FALSE) { + Ns_Log(Error, + "q_data_update_msg_status: Message id %s in group %s not found", + msg_ptr->msg_id, + group_ptr->grp_name); + return NS_FALSE; + } else { + Q_DATA_LIST_DELETE(\ + by_stat_entry_ptr, + &group_ptr->queue_by_stat_ll[msg_ptr->status]); + msg_ptr->status = new_status; + Q_DATA_LIST_APPEND(msg_ptr, &group_ptr->queue_by_stat_ll[msg_ptr->status]); + return NS_TRUE; + } +} + +/* + *---------------------------------------------------------------------- + * + * q_data_first_msg_entry -- + * + * Call this function to retrieve the first entry in the message queue. + * If specific_status is -1, all messages will be trawled, otherwise only + * those messages with the appropriate status. + * + * Results: + * NULL if no messages in the queue, pointer to the message otherwise. + * + *---------------------------------------------------------------------- + */ +struct q_message * +q_data_first_msg_entry(struct q_group *group_ptr, + int specific_status, + struct q_message_search *search_ptr) { + search_ptr->specific_status = specific_status; + if (specific_status == -1) { + search_ptr->cur_ptr = group_ptr->queue_ll; + } else { + search_ptr->cur_ptr = group_ptr->queue_by_stat_ll[specific_status]; + } + + if (search_ptr->cur_ptr == NULL) { + search_ptr->prev_ptr = NULL; + search_ptr->next_ptr = NULL; + return NULL; + } else { + search_ptr->prev_ptr = search_ptr->cur_ptr->prev_ptr; + search_ptr->next_ptr = search_ptr->cur_ptr->next_ptr; + return search_ptr->cur_ptr->msg_ptr; + } +} + + +/* + *---------------------------------------------------------------------- + * + * q_data_next_msg_entry -- + * + * Called to move to the next message in the linked list search. + * Results: + * NULL if no further messages in the queue, pointer to the message + * otherwise. + * + *---------------------------------------------------------------------- + */ +struct q_message * +q_data_next_msg_entry(struct q_message_search *search_ptr) { + search_ptr->cur_ptr = search_ptr->next_ptr; + if (search_ptr->cur_ptr == NULL) { + search_ptr->prev_ptr = NULL; + search_ptr->next_ptr = NULL; + return NULL; + } else { + search_ptr->prev_ptr = search_ptr->cur_ptr->prev_ptr; + search_ptr->next_ptr = search_ptr->cur_ptr->next_ptr; + return search_ptr->cur_ptr->msg_ptr; + } +} Index: openacs-4/contrib/misc/smsc/qcluster/cluster.h =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/cluster.h,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/qcluster/cluster.h 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,176 @@ +/* + * This file is part of QCluster. + * + * QCluster is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * QCluster is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QCluster; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: cluster.h,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +#ifndef _CLUSTER_H +#define _CLUSTER_H + +#define Version "1.3" + +#define DEBUG 1 + +/* + * General definitions + */ +#define Q_MAX_NAME_SIZE 256 /* String size to use for */ + /* character arrays used for */ + /* names */ +#define Q_MAX_SERVERS 64 /* Maximum number of clustered */ + /* servers allowed */ + +/* + * Message status definitions. + */ +#define Q_MSG_STATUS_READY 0 +#define Q_MSG_STATUS_DELAYED 1 +#define Q_MSG_STATUS_PROCESSING 2 +#define Q_MSG_STATUS_COMPLETE 3 +#define Q_MSG_STATUS_FAILED 4 +#define Q_MSG_STATUS_MAX 5 + +#define Q_MAX_MSG_SIZE 10240 /* Maximum message size */ + /* to be received from socket */ + +struct q_message_type { + int msg_type_id; /* Message type identifier */ + char type_name[Q_MAX_NAME_SIZE]; /* Name of message type */ + int (*construct_in_func)(void *, int, void *, int*); + /* Pointer to function that will */ + /* be called for transfer in of this */ + /* message type's message. */ + int (*construct_out_func)(void *, int, void *, int*); + /* Pointer to function that will */ + /* be called for transfer out of this */ + /* message type's message. */ +}; + +struct q_message { + char msg_id[Q_MAX_NAME_SIZE]; /* Unique message id */ + void *msg_ptr; /* Pointer to message contents */ + int msg_size; /* Size of message */ + time_t timestamp; /* Timestamp of message */ + int status; /* Status of message: */ + /* Q_MSG_STATUS_READY */ + /* Q_MSG_STATUS_DELAYED */ + /* Q_MSG_STATUS_PROCESSING */ + /* Q_MSG_STATUS_COMPLETE */ + struct q_group *grp_ptr; /* Pointer to queue group */ + struct q_message_type *type_ptr; /* Pointer to structure defining */ + /* the message type of this message */ +}; + +/* + * The next two structures are used to implement the linked list for + * message queues, and iteration over those lists. + */ +struct q_message_entry { + struct q_message *msg_ptr; + struct q_message_entry *prev_ptr; + struct q_message_entry *next_ptr; +}; + +struct q_message_search { + int specific_status; + struct q_message_entry *prev_ptr; + struct q_message_entry *cur_ptr; + struct q_message_entry *next_ptr; +}; + +struct q_group { + char grp_name[Q_MAX_NAME_SIZE]; /* Name of queue group */ + int grp_id; /* Unique id of queue group */ + int can_process_p; /* Flag indicating whether this */ + /* server has links to process this */ + /* message group */ + Ns_Cond event; /* Thread event structure */ + Ns_Mutex event_lock; /* Thread event lock structure */ + struct q_message_entry *queue_ll; /* Message queue linked list */ + struct q_message_entry *queue_by_stat_ll[Q_MSG_STATUS_MAX]; + /* Message queue (grouped by status) */ + /* linked list */ + Tcl_HashTable queue_ht; /* Message queue lookup hashtable */ +}; + +struct q_server { + char svr_ip[Q_MAX_NAME_SIZE]; /* The IP address of the foreign */ + /* server. */ + char svr_name[Q_MAX_NAME_SIZE]; /* The unique name of the foreign */ + /* server. */ + int iam_p; /* Flag indicating whether this */ + /* server entry is us. */ + int l_port; /* Port on which this server is */ + /* listening. */ + + /* client connection information (outgoing)*/ + SOCKET c_sock; + struct sockaddr_in c_sa; + Ns_Mutex c_mutex; + + /* server connection information (incoming)*/ + Ns_Mutex s_mutex; + SOCKET s_sock; + struct sockaddr_in s_sa; + + Tcl_HashTable groups_ht; /* Server groups */ +}; + +/* + * Global qcluster data + */ +extern Ns_RWLock g_cluster_mutex; +extern Tcl_HashTable g_cluster; +extern struct q_server *g_cluster_order[Q_MAX_SERVERS]; +extern int g_cluster_size; +extern int g_this_server_pos; +extern char *g_addr; +extern char *g_iam; +extern struct q_server *g_server; +extern int g_port; +extern int g_qcluster_started; +extern Ns_Mutex g_recover_mutex; + +/* + * Function declarations + */ +extern int +q_data_add_msg(struct q_group *group_ptr, + struct q_message *msg_ptr); +extern int +q_data_delete_msg(struct q_group *group_ptr, + char *msg_id, + int free_msg_ptr_p, + int free_payload_ptr_p); +extern struct q_message * +q_data_get_msg_entry(struct q_group *group_ptr, + char *msg_id); +extern int +q_data_update_msg_status(struct q_group *group_ptr, + struct q_message *msg_ptr, + int new_status); +extern struct q_message * +q_data_first_msg_entry(struct q_group *group_ptr, + int specific_status, + struct q_message_search *search_ptr); +extern struct q_message * +q_data_next_msg_entry(struct q_message_search *search_ptr); + + +#endif Index: openacs-4/contrib/misc/smsc/qcluster/clustercomms.c =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/clustercomms.c,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/qcluster/clustercomms.c 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,1306 @@ +/* + * This file is part of QCluster. + * + * QCluster is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * QCluster is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QCluster; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: clustercomms.c,v 1.1 2003/12/02 06:17:38 rmello Exp $ + * + * ------------------------------------------------------------------------ + * + * Message processing support for inter-qcluster server messages. + */ + +static char rcsid[] = "@(#) $Id: clustercomms.c,v 1.1 2003/12/02 06:17:38 rmello Exp $"; + +#include "ns.h" +#include "cluster.h" +#include "message.h" +#include "locks.h" +#include "clustercomms.h" + +int server_send(struct q_server *server_ptr, + const void *msg_buf, int msg_size) +{ + int write_len; + int write_val; + write_len = 0; + write_val = 0; + + Ns_MutexLock(&server_ptr->c_mutex); + while (write_len < msg_size) { + if ((write_val = send(server_ptr->c_sock, + msg_buf + write_len, + msg_size - write_len,0)) <= 0) { + write_len = -1; + break; + } else { + write_len += write_val; + } + } + Ns_MutexUnlock(&server_ptr->c_mutex); + + return write_len; +} + +#define Q_ALIGNED_SIZE(s) (s + (sizeof(int) - (s % sizeof(int)))) + + +static void q_comms_handle_msg_add(struct q_server *server_ptr, + char *msg_buf, int msg_size); +static void q_comms_handle_msg_delegate(struct q_server *server_ptr, + char *msg_buf, int msg_size); +static void q_comms_handle_status_update_msg(struct q_server *server_ptr, + char *msg_buf, int msg_size); +static void q_comms_handle_can_process_msg(struct q_server *server_ptr, + char *msg_buf, int msg_size); +static void q_comms_handle_delete_recovered_msg(struct q_server *server_ptr, + char *msg_buf, int msg_size); +static void q_comms_build_msg_add_msg(unsigned char *msg_buf, + int *msg_size_ptr, + struct q_message *msg_ptr); +static void q_comms_build_can_process_msg(unsigned char *msg_buf, + int *msg_size_ptr, + struct q_group *group_ptr); +static void q_comms_build_delegate_msg(unsigned char *msg_buf, + int *msg_size_ptr, + struct q_message *msg_ptr, int cur_hops); + +/* + *---------------------------------------------------------------------- + * + * q_comms_process -- + * + * Processes an incoming message on a cluster comms link. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +void +q_comms_process (struct q_server *server_ptr, + int msg_id, void *msg_buf, int msg_size) { + Ns_Log(Notice, + "qcluster: Message %d received of size %d.", msg_id, msg_size); + switch (msg_id) { + case Q_MSG_TYPE_ID: + Ns_Log(Error, + "qcluster: Unexpected server identification message, ignoring"); + break; + case Q_MSG_TYPE_MSG_ADD: + q_comms_handle_msg_add(server_ptr, msg_buf, msg_size); + break; + case Q_MSG_TYPE_MSG_DELEGATE: + q_comms_handle_msg_delegate(server_ptr, msg_buf, msg_size); + break; + case Q_MSG_TYPE_MSG_STATUS_UPDATE: + q_comms_handle_status_update_msg(server_ptr, msg_buf, msg_size); + break; + case Q_MSG_TYPE_MSG_CAN_PROCESS: + q_comms_handle_can_process_msg(server_ptr, msg_buf, msg_size); + break; + case Q_MSG_TYPE_MSG_DELETE_RECOVERED: + q_comms_handle_delete_recovered_msg(server_ptr, msg_buf, msg_size); + break; + default: + Ns_Log(Error, + "qcluster: Unknown server message from %d (%d), ignoring", + server_ptr->svr_name, msg_id); + break; + } +} + + +/* + *---------------------------------------------------------------------- + * + * q_comms_handle_msg_add -- + * + * Processes an incoming Q_MSG_TYPE_MSG_ADD message. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +static void +q_comms_handle_msg_add(struct q_server *server_ptr, + char *msg_buf, int msg_size) { + int msg_id_len; + int grp_name_len; + char msg_id[Q_MAX_NAME_SIZE]; + char grp_name[Q_MAX_NAME_SIZE]; + struct q_group *group_ptr; + int payload_size; + char decoded_msg[Q_MAX_MSG_SIZE]; + int decoded_size; + int msg_type_id; + struct q_message_type *msg_type_ptr; + int msg_status; + int msg_pos; + +// Ns_Log(Notice, "q_comms_handle_msg_add: entering"); + + msg_pos = 0; + /* get message id */ + msg_id_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(msg_id, &msg_buf[msg_pos], msg_id_len); + msg_id[msg_id_len] = '\0'; + msg_pos += Q_ALIGNED_SIZE(msg_id_len); + /* get group name */ + grp_name_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(grp_name, &msg_buf[msg_pos], grp_name_len); + grp_name[grp_name_len] = '\0'; + msg_pos += Q_ALIGNED_SIZE(grp_name_len); + /* get type id */ + msg_type_id = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* get message status */ + msg_status = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + + /* + * Lookup the message type. + */ + msg_type_ptr = q_get_msg_type_ptr(msg_type_id); + if (msg_type_ptr == NULL) { + Ns_Log(Error, + "Unknown message type id (%d) in q_comms_handle_msg_add, ignoring message", + msg_type_id); + return; + } + + /* + * Lookup group. + */ + group_ptr = q_get_group_ptr(server_ptr, grp_name); + if (group_ptr == NULL) { + Ns_Log(Error, + "Unknown group name (%s) in q_comms_handle_msg_add, ignoring message", + grp_name); + return; + } + /* get payload size */ + payload_size = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* get the payload */ + msg_type_ptr->construct_in_func(&msg_buf[msg_pos], + payload_size, + decoded_msg, + &decoded_size); + /* + * Call the function to add the message + */ + q_remote_queue_msg(msg_type_ptr, group_ptr, + msg_id, msg_status, decoded_msg, decoded_size); +// Ns_Log(Notice, "q_comms_handle_msg_add: exiting"); +} + +/* + *---------------------------------------------------------------------- + * + * q_comms_handle_msg_delegate -- + * + * Processes an incoming Q_MSG_TYPE_MSG_DELEGATE message. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +static void +q_comms_handle_msg_delegate(struct q_server *server_ptr, + char *msg_buf, int msg_size) { + int msg_id_len; + int grp_name_len; + char msg_id[Q_MAX_NAME_SIZE]; + char grp_name[Q_MAX_NAME_SIZE]; + struct q_group *group_ptr; + int payload_size; + char decoded_msg[Q_MAX_MSG_SIZE]; + int decoded_size; + int msg_type_id; + struct q_message_type *msg_type_ptr; + int msg_status; + int msg_pos; + int max_hops; + +// Ns_Log(Notice, "q_comms_handle_msg_delegate: entering"); + + msg_pos = 0; + /* get message id */ + msg_id_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(msg_id, &msg_buf[msg_pos], msg_id_len); + msg_id[msg_id_len] = '\0'; + msg_pos += Q_ALIGNED_SIZE(msg_id_len); + /* get group name */ + grp_name_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(grp_name, &msg_buf[msg_pos], grp_name_len); + grp_name[grp_name_len] = '\0'; + msg_pos += Q_ALIGNED_SIZE(grp_name_len); + /* get type id */ + msg_type_id = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* get message status */ + msg_status = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* get max hops */ + max_hops = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + + /* + * Lookup the message type. + */ + msg_type_ptr = q_get_msg_type_ptr(msg_type_id); + if (msg_type_ptr == NULL) { + Ns_Log(Error, + "Unknown message type id (%d) in q_comms_handle_msg_delegate, ignoring message", + msg_type_id); + return; + } + + /* + * Lookup group. + */ + group_ptr = q_get_group_ptr(g_server, grp_name); + if (group_ptr == NULL) { + Ns_Log(Error, + "Unknown group name (%s) in q_comms_handle_msg_delegate, ignoring message", + grp_name); + return; + } + /* get payload size */ + payload_size = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* get the payload */ + msg_type_ptr->construct_in_func(&msg_buf[msg_pos], + payload_size, + decoded_msg, + &decoded_size); + /* + * Call the q_queue_msg function to try and queue the delegated message. + */ + q_queue_msg(msg_type_ptr, group_ptr, + msg_id, msg_status, + decoded_msg, decoded_size, + max_hops); +// Ns_Log(Notice, "q_comms_handle_msg_delegate: exiting"); + return; +} + +/* + *---------------------------------------------------------------------- + * + * q_comms_handle_status_update_msg -- + * + * Processes an incoming Q_MSG_TYPE_STATUS_UPDATE_MSG message. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +static void +q_comms_handle_status_update_msg(struct q_server *server_ptr, + char *msg_buf, int msg_size) { + int msg_id_len; + int grp_name_len; + char msg_id[Q_MAX_NAME_SIZE]; + char grp_name[Q_MAX_NAME_SIZE]; + struct q_group *group_ptr; + int payload_size; + char decoded_msg[Q_MAX_MSG_SIZE]; + int decoded_size; + int msg_status; + int msg_pos; +// Ns_Log(Notice, "q_comms_handle_status_update_msg: entering"); + + msg_pos = 0; + /* get message id */ + msg_id_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(msg_id, &msg_buf[msg_pos], msg_id_len); + msg_id[msg_id_len] = '\0'; + msg_pos += Q_ALIGNED_SIZE(msg_id_len); + /* get group name */ + grp_name_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(grp_name, &msg_buf[msg_pos], grp_name_len); + grp_name[grp_name_len] = '\0'; + msg_pos += Q_ALIGNED_SIZE(grp_name_len); + /* get message status */ + msg_status = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + + + /* + * Lookup group. + */ + group_ptr = q_get_group_ptr(server_ptr, grp_name); + if (group_ptr == NULL) { + Ns_Log(Error, + "q_comms_handle_status_update_msg: Unknown group name (%s), ignoring message", + grp_name); + return; + } + q_remote_msg_status_update(msg_id, group_ptr, + msg_status); +// Ns_Log(Notice, "q_comms_handle_status_update_msg: exiting"); + return; +} + +/* + *---------------------------------------------------------------------- + * + * q_comms_handle_delete_recovered_msg -- + * + * Processes an incoming Q_MSG_TYPE_DELETE_RECOVERED message. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +static void +q_comms_handle_delete_recovered_msg(struct q_server *server_ptr, + char *msg_buf, int msg_size) { + int msg_id_len; + int grp_name_len; + int svr_name_len; + char msg_id[Q_MAX_NAME_SIZE]; + char grp_name[Q_MAX_NAME_SIZE]; + char svr_name[Q_MAX_NAME_SIZE]; + struct q_group *group_ptr; + int msg_status; + int msg_pos; + int server_pos; + int count; +// Ns_Log(Notice, "q_comms_handle_delete_recovered_msg: entering"); + + msg_pos = 0; + /* get message id */ + msg_id_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(msg_id, &msg_buf[msg_pos], msg_id_len); + msg_id[msg_id_len] = '\0'; + msg_pos += Q_ALIGNED_SIZE(msg_id_len); + /* get server name */ + svr_name_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(svr_name, &msg_buf[msg_pos], svr_name_len); + svr_name[svr_name_len] = '\0'; + msg_pos += Q_ALIGNED_SIZE(svr_name_len); + /* get group name */ + grp_name_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(grp_name, &msg_buf[msg_pos], grp_name_len); + grp_name[grp_name_len] = '\0'; + msg_pos += Q_ALIGNED_SIZE(grp_name_len); + + /* + * Lookup server + */ + server_pos = -1; + for (count = 0; count < g_cluster_size; count++) { + if (!strcmp(svr_name, g_cluster_order[count]->svr_name)) { + server_pos = count; + } + } + + if (server_pos == -1) { + Ns_Log(Error, + "q_comms_handle_delete_recovered_msg: Unknown server name (%s), ignoring message", + svr_name); + return; + } + + /* + * Lookup group. + */ + group_ptr = q_get_group_ptr(g_cluster_order[server_pos], grp_name); + if (group_ptr == NULL) { + Ns_Log(Error, + "q_comms_handle_delete_recovered_msg: Unknown group name (%s), ignoring message", + grp_name); + return; + } + q_remote_msg_status_update(msg_id, group_ptr, + Q_MSG_STATUS_COMPLETE); +// Ns_Log(Notice, "q_comms_handle_delete_recovered_msg: exiting"); + return; +} + +/* + *---------------------------------------------------------------------- + * + * q_comms_handle_can_process_msg -- + * + * Processes an incoming Q_MSG_TYPE_CAN_PROCESS message. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +static void +q_comms_handle_can_process_msg(struct q_server *server_ptr, + char *msg_buf, int msg_size) { + int grp_name_len; + char grp_name[Q_MAX_NAME_SIZE]; + struct q_group *group_ptr; + int can_process_val; + int msg_pos; +// Ns_Log(Notice, "q_comms_handle_can_process_msg: entering"); + + msg_pos = 0; + /* get group name */ + grp_name_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(grp_name, &msg_buf[msg_pos], grp_name_len); + grp_name[grp_name_len] = '\0'; + msg_pos += Q_ALIGNED_SIZE(grp_name_len); + /* get can_process value */ + can_process_val = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + + /* + * Lookup group. + */ + group_ptr = q_get_group_ptr(server_ptr, grp_name); + if (group_ptr == NULL) { + Ns_Log(Error, + "q_comms_handle_can_process_msg: Unknown group name (%s), ignoring message", + grp_name); + return; + } + + q_remote_set_can_process_val(server_ptr, group_ptr, can_process_val); +// Ns_Log(Notice, "q_comms_handle_can_process_msg: exiting"); + return; +} + +/* + *---------------------------------------------------------------------- + * + * q_comms_build_msg_add_msg -- + * + * Builds a Q_MSG_TYPE_MSG_ADD message. + * + * Results: + * NS_TRUE + * + *---------------------------------------------------------------------- + */ +static void q_comms_build_msg_add_msg(unsigned char *msg_buf, + int *msg_size_ptr, + struct q_message *msg_ptr) { + int payload_size; + int msg_size; + + /* + * Construct the message to be sent. + * Message is of type Q_MSG_TYPE_MSG_ADD: + */ + msg_size = 0; + /* Message id */ + *((int *)&msg_buf[msg_size]) = htonl(Q_MSG_TYPE_MSG_ADD); + msg_size+= sizeof(int); + /* Message size (skip this and fill in at the end */ + msg_size+= sizeof(int); + /* message id */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(msg_ptr->msg_id)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], msg_ptr->msg_id); + msg_size+= Q_ALIGNED_SIZE(strlen(msg_ptr->msg_id)); + /* group name size, and group name */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(msg_ptr->grp_ptr->grp_name)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], msg_ptr->grp_ptr->grp_name); + msg_size+= Q_ALIGNED_SIZE(strlen(msg_ptr->grp_ptr->grp_name)); + /* message type id */ + *((int *)&msg_buf[msg_size]) = htonl(msg_ptr->type_ptr->msg_type_id); + msg_size+= sizeof(int); + /* message status */ + *((int *)&msg_buf[msg_size]) = htonl(msg_ptr->status); + msg_size+= sizeof(int); + /* message payload */ + msg_ptr->type_ptr->construct_out_func(msg_ptr->msg_ptr, + msg_ptr->msg_size, + &msg_buf[msg_size+sizeof(int)], + &payload_size); + *((int *)&msg_buf[msg_size]) = htonl(payload_size); + msg_size+= Q_ALIGNED_SIZE(payload_size) + sizeof(int); + /* Set the total message size */ + *((int *)&msg_buf[sizeof(int)]) = htonl(msg_size - sizeof(int)); + msg_size+= sizeof(int); + + *msg_size_ptr = msg_size; +} + +/* + *---------------------------------------------------------------------- + * + * q_comms_send_add_msg -- + * + * Sends a Q_MSG_TYPE_MSG_ADD message to a specified server. + * + * Results: + * NS_TRUE + * + *---------------------------------------------------------------------- + */ +int q_comms_send_add_msg(struct q_server *server_ptr, + struct q_message *msg_ptr) { + char msg_buf[Q_MAX_MSG_SIZE]; + int msg_size; + +// Ns_Log(Notice, "q_comms_distribute_msg: entering (%d)",Q_MAX_MSG_SIZE); + /* + * Build the message. + */ + q_comms_build_msg_add_msg(msg_buf, + &msg_size, + msg_ptr); + + /* + * Sending the message to the server, making sure we're connected first. + */ + q_get_rlock(server_ptr); + if (server_ptr->c_sock != -1) { + /* + * Lets send the message. + */ + if (server_send(server_ptr, msg_buf, msg_size) == -1) { + Ns_Log(Error,"q_comms_send_add_msg: send error to %s", + server_ptr->svr_name); + } + } else { + Ns_Log(Error, + "q_comms_send_add_msg: Send attempted to a disconnected server; %s", + server_ptr->svr_name); + } + q_release_lock(server_ptr); + return NS_TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * q_comms_distribute_msg -- + * + * Distrubutes a Q_MSG_TYPE_MSG_ADD message amoungst all connected + * servers. + * + * Results: + * NS_TRUE + * + *---------------------------------------------------------------------- + */ +int q_comms_distribute_msg(struct q_server *ignored_server_ptr, + struct q_message *msg_ptr) { + int count; + struct q_server *server_ptr; + char msg_buf[Q_MAX_MSG_SIZE]; + int msg_size; + +// Ns_Log(Notice, "q_comms_distribute_msg: entering (%d)",Q_MAX_MSG_SIZE); + /* + * Build the message. + */ + q_comms_build_msg_add_msg(msg_buf, + &msg_size, + msg_ptr); + + /* + * Loop through each server, sending the constructed message. Note that + * servers that are currently disconnected are ignored (of course this + * includes this one). + */ + for (count = 0; count < g_cluster_size; count++) { + server_ptr = g_cluster_order[count]; + + if (server_ptr != g_server && + server_ptr != ignored_server_ptr) { + q_get_rlock(server_ptr); + if (server_ptr->c_sock != -1) { + /* + * Lets send the message. + */ + if (server_send(server_ptr, msg_buf, msg_size) == -1) { + Ns_Log(Error,"q_comms_distribute_msg: send error to %s", + server_ptr->svr_name); + } + } + q_release_lock(server_ptr); + } + } + return NS_TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * q_comms_build_delegate_msg -- + * + * Builds a delegate message. + * + * Results: + * NS_TRUE + * + *---------------------------------------------------------------------- + */ +static void q_comms_build_delegate_msg(unsigned char *msg_buf, + int *msg_size_ptr, + struct q_message *msg_ptr, int cur_hops) +{ + int msg_size; + int cur_pos; + int payload_size; + + /* + * Construct the message to be sent. + * Message is of type Q_MSG_TYPE_MSG_ADD: + */ + msg_size = 0; + /* Message id */ + *((int *)&msg_buf[msg_size]) = htonl(Q_MSG_TYPE_MSG_DELEGATE); + msg_size+= sizeof(int); + /* Message size (skip this and fill in at the end */ + msg_size+= sizeof(int); + /* message id */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(msg_ptr->msg_id)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], msg_ptr->msg_id); + msg_size+= Q_ALIGNED_SIZE(strlen(msg_ptr->msg_id)); + /* group name size, and group name */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(msg_ptr->grp_ptr->grp_name)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], msg_ptr->grp_ptr->grp_name); + msg_size+= Q_ALIGNED_SIZE(strlen(msg_ptr->grp_ptr->grp_name)); + /* message type id */ + *((int *)&msg_buf[msg_size]) = htonl(msg_ptr->type_ptr->msg_type_id); + msg_size+= sizeof(int); + /* message status */ +// *((int *)&msg_buf[msg_size]) = htonl(msg_ptr->status); + *((int *)&msg_buf[msg_size]) = htonl(Q_MSG_STATUS_READY); + msg_size+= sizeof(int); + /* max hops field */ + *((int *)&msg_buf[msg_size]) = htonl(cur_hops); + msg_size+= sizeof(int); + /* message payload */ + msg_ptr->type_ptr->construct_out_func(msg_ptr->msg_ptr, + msg_ptr->msg_size, + &msg_buf[msg_size+sizeof(int)], + &payload_size); + *((int *)&msg_buf[msg_size]) = htonl(payload_size); + msg_size+= Q_ALIGNED_SIZE(payload_size) + sizeof(int); + /* Set the total message size */ + *((int *)&msg_buf[sizeof(int)]) = htonl(msg_size - sizeof(int)); + msg_size+= sizeof(int); + + *msg_size_ptr = msg_size; + return; +} + + +/* + *---------------------------------------------------------------------- + * + * q_comms_send_delegate_msg -- + * + * Sends a delegate message to a specified server. Decrements the + * cur_hops parameter before it sends the message. + * + * Results: + * NS_TRUE - Successfully delegated + * NS_FALSE - Unsuccessfully delegated + * + *---------------------------------------------------------------------- + */ +int q_comms_send_delegate_msg(struct q_server *remote_server_ptr, + struct q_message *msg_ptr, int cur_hops) { + int count; + struct q_server *server_ptr; + char msg_buf[Q_MAX_MSG_SIZE]; + int msg_size; + int cur_pos; + int sent_flag; + int payload_size; + +// Ns_Log(Notice, "q_comms_send_delegate_msg: Entering"); + /* + * If we've reached the maximum number of hops allowed for this message, + * then return the failure notice for this deligation operation. + */ + if (cur_hops == 0) { + return NS_FALSE; + } + + cur_hops--; + + /* + * Construct the message to be sent. + */ + q_comms_build_delegate_msg(msg_buf, &msg_size, + msg_ptr, cur_hops); + + q_get_rlock(remote_server_ptr); + if (remote_server_ptr->c_sock != -1) { + /* + * Lets send the message. + */ + if (server_send(remote_server_ptr, msg_buf, msg_size) == -1) { + Ns_Log(Error,"q_comms_send_delegate_msg: send error to %s", + remote_server_ptr->svr_name); + } else { + sent_flag = 1; + } + } else { + Ns_Log(Error, + "q_comms_send_delegate_msg: Send attempted to a disconnected server; %s", + remote_server_ptr->svr_name); + } + q_release_lock(remote_server_ptr); + + if (sent_flag) { + return NS_TRUE; + } else { + return NS_FALSE; + } +} + + +/* + *---------------------------------------------------------------------- + * + * q_comms_delegate_msg -- + * + * Passes a message onto the nearest neighbour server. Decrements the + * cur_hops parameter before it sends the message. + * The ignored_server_ptr may be populated if a particular server in + * the server list should be ignored. NULL otherwise. + * + * Results: + * NS_TRUE + * + *---------------------------------------------------------------------- + */ +int q_comms_delegate_msg(struct q_server *ignored_server_ptr, + struct q_message *msg_ptr, int cur_hops) { + int count; + struct q_server *server_ptr; + char msg_buf[Q_MAX_MSG_SIZE]; + int msg_size; + int cur_pos; + int sent_flag; + int payload_size; + +// Ns_Log(Notice, "q_comms_delegate_msg: Entering"); + /* + * If we've reached the maximum number of hops allowed for this message, + * then return the failure notice for this deligation operation. + */ + if (cur_hops == 0) { + return NS_FALSE; + } + + cur_hops--; + + /* + * Construct the message to be sent. + */ + q_comms_build_delegate_msg(msg_buf, &msg_size, + msg_ptr, cur_hops); + + /* + * Loop until we find our nearest neighbour, or we loop back round to + * this server (ie, no connected servers exist). + */ + cur_pos = (g_this_server_pos + 1) % g_cluster_size; + sent_flag = 0; + while (cur_pos != g_this_server_pos && !sent_flag) { + server_ptr = g_cluster_order[cur_pos]; + if (server_ptr != ignored_server_ptr) { + q_get_rlock(server_ptr); + if (server_ptr->c_sock != -1) { + /* + * Lets send the message. + */ + if (server_send(server_ptr, msg_buf, msg_size) == -1) { + Ns_Log(Error,"q_comms_delegate_msg: send error to %s", + server_ptr->svr_name); + } + sent_flag = 1; + } + q_release_lock(server_ptr); + } + cur_pos = (cur_pos + 1) % g_cluster_size; + } + if (sent_flag) { + return NS_TRUE; + } else { + return NS_FALSE; + } +} + + +/* + *---------------------------------------------------------------------- + * + * q_comms_msg_status_update -- + * + * Distrubutes a Q_MSG_TYPE_STATUS_UPDATE message amoungst all connected + * servers. + * + * Results: + * NS_TRUE + * + *---------------------------------------------------------------------- + */ +int q_comms_msg_status_update(struct q_message *msg_ptr) { + int count; + struct q_server *server_ptr; + char msg_buf[Q_MAX_MSG_SIZE]; + int msg_size; + int n; + + /* + * Construct the message to be sent. + * Message is of type Q_MSG_TYPE_MSG_STATUS_UPDATE: + */ + msg_size = 0; + /* Message id */ + *((int *)&msg_buf[msg_size]) = htonl(Q_MSG_TYPE_MSG_STATUS_UPDATE); + msg_size+= sizeof(int); + /* Message size (skip this and fill in at the end */ + msg_size+= sizeof(int); + /* message id */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(msg_ptr->msg_id)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], msg_ptr->msg_id); + msg_size+= Q_ALIGNED_SIZE(strlen(msg_ptr->msg_id)); + /* group name size, and group name */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(msg_ptr->grp_ptr->grp_name)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], msg_ptr->grp_ptr->grp_name); + msg_size+= Q_ALIGNED_SIZE(strlen(msg_ptr->grp_ptr->grp_name)); + /* message status */ + *((int *)&msg_buf[msg_size]) = htonl(msg_ptr->status); + msg_size+= sizeof(int); + /* Set the total message size */ + *((int *)&msg_buf[sizeof(int)]) = htonl(msg_size - sizeof(int)); + msg_size+= sizeof(int); + + /* + * Loop through each server, sending the constructed message. Note that + * servers that are currently disconnected are ignored (of course this + * includes this one). + */ + for (count = 0; count < g_cluster_size; count++) { + server_ptr = g_cluster_order[count]; + + if (server_ptr != g_server) { + q_get_rlock(server_ptr); + if (server_ptr->c_sock != -1) { + /* + * Lets send the message. + */ + if (server_send(server_ptr, msg_buf, msg_size) == -1) { + Ns_Log(Error,"q_comms_msg_status_update: send error to %s", + server_ptr->svr_name); + } + } + q_release_lock(server_ptr); + } + } + return NS_TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * q_comms_build_can_process_msg -- + * + * Builds a Q_MSG_TYPE_MSG_CAN_PROCESS message. + * + * Results: + * NS_TRUE + * + *---------------------------------------------------------------------- + */ +static void q_comms_build_can_process_msg(unsigned char *msg_buf, + int *msg_size_ptr, + struct q_group *group_ptr) { + int payload_size; + int msg_size; + + /* + * Construct the message to be sent. + * Message is of type Q_MSG_TYPE_CAN_PROCESS: + */ + msg_size = 0; + /* Message id */ + *((int *)&msg_buf[msg_size]) = htonl(Q_MSG_TYPE_MSG_CAN_PROCESS); + msg_size+= sizeof(int); + /* Message size (skip this and fill in at the end */ + msg_size+= sizeof(int); + /* group name size, and group name */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(group_ptr->grp_name)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], group_ptr->grp_name); + msg_size+= Q_ALIGNED_SIZE(strlen(group_ptr->grp_name)); + /* can_process_val */ + *((int *)&msg_buf[msg_size]) = htonl(group_ptr->can_process_p); + msg_size+= sizeof(int); + /* Set the total message size */ + *((int *)&msg_buf[sizeof(int)]) = htonl(msg_size - sizeof(int)); + msg_size+= sizeof(int); + + *msg_size_ptr = msg_size; +} + +/* + *---------------------------------------------------------------------- + * + * q_comms_can_process_msg -- + * + * Distrubutes a Q_MSG_TYPE_CAN_PROCESS message amoungst all connected + * servers. + * + * Results: + * NS_TRUE + * + *---------------------------------------------------------------------- + */ +int q_comms_can_process_msg(struct q_group *group_ptr) { + int count; + struct q_server *server_ptr; + char msg_buf[Q_MAX_MSG_SIZE]; + int msg_size; + + /* + * Construct the message to be sent. + */ + q_comms_build_can_process_msg(msg_buf, + &msg_size, + group_ptr); + + /* + * Loop through each server, sending the constructed message. Note that + * servers that are currently disconnected are ignored (of course this + * includes this one). + */ + for (count = 0; count < g_cluster_size; count++) { + server_ptr = g_cluster_order[count]; + + if (server_ptr != g_server) { + q_get_rlock(server_ptr); + if (server_ptr->c_sock != -1) { + /* + * Lets send the message. + */ + if (server_send(server_ptr, msg_buf, msg_size) == -1) { + Ns_Log(Error,"q_comms_can_process_msg: send error to %s", + server_ptr->svr_name); + } + } + q_release_lock(server_ptr); + } + } + return NS_TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * q_comms_send_can_process_msg -- + * + * Sends a Q_MSG_TYPE_CAN_PROCESS message to a specified remote server. + * + * Results: + * NS_TRUE + * + *---------------------------------------------------------------------- + */ +int q_comms_send_can_process_msg(struct q_server *server_ptr, + struct q_group *group_ptr) { + int count; + char msg_buf[Q_MAX_MSG_SIZE]; + int msg_size; + + /* + * Construct the message to be sent. + */ + q_comms_build_can_process_msg(msg_buf, + &msg_size, + group_ptr); + + /* + * Send the constructed message. + */ + q_get_rlock(server_ptr); + if (server_ptr->c_sock != -1) { + /* + * Lets send the message. + */ + if (server_send(server_ptr, msg_buf, msg_size) == -1) { + Ns_Log(Error,"q_comms_can_process_msg: send error to %s", + server_ptr->svr_name); + } + } else { + Ns_Log(Error, + "q_comms_send_can_process_msg: Send attempted to a disconnected server; %s", + server_ptr->svr_name); + } + q_release_lock(server_ptr); + return NS_TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * q_server_connect_dump_messages -- + * + * Called when a client connect succeeds to a remote server. This function + * loops through all local queues, sending a MESSAGE_ADD message, + * thus populating the newly connected servers own copy of this servers + * queue. + * + * Results: + * + *---------------------------------------------------------------------- + */ +int +q_comms_server_connect_dump_messages(struct q_server *remote_server_ptr) { + struct q_group *group_ptr; + Tcl_HashEntry *grp_entry_ptr; + Tcl_HashEntry *grp_search_ptr; + Tcl_HashSearch grp_search; + struct q_message *message_ptr; + struct q_message_search msg_search; + + q_get_wlock(g_server); + Ns_Log(Notice,"Dumping queued messages to server %s", + remote_server_ptr->svr_name); + + grp_search_ptr = Tcl_FirstHashEntry(&g_server->groups_ht, + &grp_search); + while (grp_search_ptr != NULL) { + group_ptr = Tcl_GetHashValue(grp_search_ptr); + q_get_rlock(group_ptr); + + q_comms_send_can_process_msg(remote_server_ptr, + group_ptr); + + Ns_Log(Notice,"Dumping group %s", group_ptr->grp_name); + message_ptr = q_data_first_msg_entry(group_ptr, + -1, + &msg_search); + while (message_ptr != NULL) { + Ns_Log(Notice,"Dumping message %s", message_ptr->msg_id); + /* + * Recover this individual message. + */ + q_comms_send_add_msg(remote_server_ptr, + message_ptr); + message_ptr = q_data_next_msg_entry(&msg_search); + } + q_release_lock(group_ptr); + grp_search_ptr = Tcl_NextHashEntry(&grp_search); + } + q_release_lock(g_server); + return NS_TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * q_comms_recover_server -- + * + * This function is called when a connection to a remote server + * fails. The list of servers is checked and if we're the nearest + * neighbour to the failed server, recover each message. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +int +q_comms_recover_server(struct q_server *failed_server_ptr) { + int cur_pos; + int complete_flag; + int connected_count; /* Count of the number of connected servers */ + /* between this one and the failed server */ + struct q_server *server_ptr; + + struct q_group *group_ptr; + Tcl_HashEntry *grp_entry_ptr; + Tcl_HashEntry *grp_search_ptr; + Tcl_HashSearch grp_search; + + struct q_message *message_ptr; + struct q_message_search msg_search; + + Ns_Log(Notice,"Recovering server %x, %s", failed_server_ptr, + failed_server_ptr->svr_name); + + /* + * Loop until we find our nearest neighbour, or we loop back round to + * this server (ie, no connected servers exist). + */ + cur_pos = (g_this_server_pos + 1) % g_cluster_size; + complete_flag = 0; + connected_count = 0; + while (cur_pos != g_this_server_pos && !complete_flag) { + server_ptr = g_cluster_order[cur_pos]; + if (server_ptr == failed_server_ptr) { + complete_flag = 1; + } else { + q_get_rlock(server_ptr); + if (server_ptr->c_sock != -1) { + connected_count++; + } + q_release_lock(server_ptr); + } + cur_pos = (cur_pos + 1) % g_cluster_size; + } + /* + * If connected_count indicates that there are no connected servers between + * this server and the failed server, then we are the nearest neighbour. + * Recover the messages. + */ + Ns_Log(Notice,"Recovering (2) server %x", failed_server_ptr); + Ns_Log(Notice,"Waiting for lock on server %x, %s", failed_server_ptr, + failed_server_ptr->svr_name); + if (connected_count == 0) { + q_get_wlock(failed_server_ptr); + Ns_Log(Notice,"Recovering server %x, %s", failed_server_ptr, + failed_server_ptr->svr_name); + + grp_search_ptr = Tcl_FirstHashEntry(&failed_server_ptr->groups_ht, + &grp_search); + while (grp_search_ptr != NULL) { + group_ptr = Tcl_GetHashValue(grp_search_ptr); + q_get_wlock(group_ptr); + Ns_Log(Notice,"Recovering group %x, %s", group_ptr, group_ptr->grp_name); + message_ptr = q_data_first_msg_entry(group_ptr, + -1, + &msg_search); + while (message_ptr != NULL) { + Ns_Log(Notice,"Recovering message %s", message_ptr->msg_id); + /* + * Recover this individual message. + */ + q_comms_delete_recovered_message(failed_server_ptr, + message_ptr); + q_recover_msg(failed_server_ptr, + message_ptr); + Ns_Log(Notice,"Recovered message %s", message_ptr->msg_id); + message_ptr = q_data_next_msg_entry(&msg_search); + } + q_release_lock(group_ptr); + grp_search_ptr = Tcl_NextHashEntry(&grp_search); + } + q_release_lock(failed_server_ptr); + } + + return NS_TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * q_comms_delete_recovered_message -- + * + * Distrubutes a Q_MSG_TYPE_MSG_DELETE_RECOVERED message amoungst all + * connected servers except the failed one. + * + * Results: + * NS_TRUE + * + *---------------------------------------------------------------------- + */ +int q_comms_delete_recovered_message(struct q_server *failed_server_ptr, + struct q_message *msg_ptr) { + int count; + struct q_server *server_ptr; + char msg_buf[Q_MAX_MSG_SIZE]; + int msg_size; + + /* + * Construct the message to be sent. + * Message is of type Q_MSG_TYPE_MSG_DELETE_RECOVERED: + */ + msg_size = 0; + /* Message id */ + *((int *)&msg_buf[msg_size]) = htonl(Q_MSG_TYPE_MSG_DELETE_RECOVERED); + msg_size+= sizeof(int); + /* Message size (skip this and fill in at the end */ + msg_size+= sizeof(int); + /* message id */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(msg_ptr->msg_id)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], msg_ptr->msg_id); + msg_size+= Q_ALIGNED_SIZE(strlen(msg_ptr->msg_id)); + /* server name size, and group name */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(failed_server_ptr->svr_name)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], failed_server_ptr->svr_name); + msg_size+= Q_ALIGNED_SIZE(strlen(failed_server_ptr->svr_name)); + /* group name size, and group name */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(msg_ptr->grp_ptr->grp_name)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], msg_ptr->grp_ptr->grp_name); + msg_size+= Q_ALIGNED_SIZE(strlen(msg_ptr->grp_ptr->grp_name)); + /* Set the total message size */ + *((int *)&msg_buf[sizeof(int)]) = htonl(msg_size - sizeof(int)); + msg_size+= sizeof(int); + + /* + * Loop through each server, sending the constructed message. Note that + * servers that are currently disconnected are ignored (of course this + * includes this one), and also ignore the failed server (so we don't + * conflict with any locks the calling procedure may have). + */ + for (count = 0; count < g_cluster_size; count++) { + server_ptr = g_cluster_order[count]; + + if (server_ptr != g_server && + server_ptr != failed_server_ptr) { + q_get_rlock(server_ptr); + if (server_ptr->c_sock != -1) { + /* + * Lets send the message. + */ + if (server_send(server_ptr, msg_buf, msg_size) == -1) { + Ns_Log(Error,"q_comms_delete_recovered_message: send error to %s", + server_ptr->svr_name); + } + } + q_release_lock(server_ptr); + } + } + return NS_TRUE; +} Index: openacs-4/contrib/misc/smsc/qcluster/clustercomms.h =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/clustercomms.h,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/qcluster/clustercomms.h 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,59 @@ +/* + * This file is part of QCluster. + * + * QCluster is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * QCluster is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QCluster; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: clustercomms.h,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +#include "ns.h" +#include "cluster.h" + +#ifndef _CLUSTERCOMMS_H +#define _CLUSTERCOMMS_H + +extern void q_comms_process(struct q_server *server_ptr, + int msg_id, void *msg_buf, int msg_size); +extern int q_comms_send_add_msg(struct q_server *remote_server_ptr, + struct q_message *msg_ptr); +extern int q_comms_send_can_process_msg(struct q_server *remote_server_ptr, + struct q_group *group_ptr); +extern int q_comms_send_delegate_msg(struct q_server *remote_server_ptr, + struct q_message *msg_ptr, int cur_hops); +extern int q_comms_can_process_msg(struct q_group *group_ptr); +extern int q_comms_distribute_msg(struct q_server *ignored_server_ptr, + struct q_message *msg_ptr); +extern int q_comms_delegate_msg(struct q_server *ignored_server_ptr, + struct q_message *msg_ptr, int cur_hops); +extern int q_comms_msg_status_update(struct q_message *msg_ptr); +extern int q_comms_recover_server(struct q_server *failed_server_ptr); +extern int q_comms_delete_recovered_message(struct q_server *failed_server_ptr, + struct q_message *msg_ptr); +extern int q_comms_server_connect_dump_messages( + struct q_server *remote_server_ptr); + +/* + * Define the qcluster protocol message indentifiers. + */ +#define Q_MSG_TYPE_ID 0 +#define Q_MSG_TYPE_MSG_ADD 1 +#define Q_MSG_TYPE_MSG_DELEGATE 2 +#define Q_MSG_TYPE_MSG_STATUS_UPDATE 3 +#define Q_MSG_TYPE_MSG_DELETE_RECOVERED 4 +#define Q_MSG_TYPE_MSG_CAN_PROCESS 5 + +#endif Index: openacs-4/contrib/misc/smsc/qcluster/gpl.txt =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/gpl.txt,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/qcluster/gpl.txt 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. Index: openacs-4/contrib/misc/smsc/qcluster/locks.c =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/locks.c,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/qcluster/locks.c 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,133 @@ +/* + * This file is part of QCluster. + * + * QCluster is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * QCluster is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QCluster; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: locks.c,v 1.1 2003/12/02 06:17:38 rmello Exp $ + * + * ------------------------------------------------------------------------ + * + * Manage locks on global data. Maintains a hashtable of object pointers, + * which are the keys, and RW locks, which are the values. Functions are + * used to get/release access to the objects. These functions use a single RW + * lock to make sure access to the locks hashtable is threadsafe. + * + * This might not be the most efficient mechanism for controlling access to + * the global data, but its certainly the cleanest I could find. + */ + +static char rcsid[] = "@(#) $Id: locks.c,v 1.1 2003/12/02 06:17:38 rmello Exp $"; + +#include "ns.h" +#include "locks.h" + +static Ns_RWLock g_locks_mutex; +static Tcl_HashTable g_locks; + +void +q_init_lock (void) { + Ns_RWLockInit(&g_locks_mutex); + Tcl_InitHashTable(&g_locks, TCL_ONE_WORD_KEYS); +} + +Ns_RWLock * +q_add_lock(void *object_ptr) +{ + Tcl_HashEntry *entry; + Ns_RWLock *new_lock_ptr; + int new_flag; + + /* + * Create the new lock for this object and initialise it. + */ + new_lock_ptr = ns_malloc(sizeof(Ns_RWLock)); + Ns_RWLockInit(new_lock_ptr); + + /* + * Get a write lock on the locks hashtable, and create the lock entry. + * Then release the write lock on the locks hashtable. + */ + Ns_RWLockWrLock(&g_locks_mutex); + entry = Tcl_CreateHashEntry(&g_locks, object_ptr, &new_flag); + Tcl_SetHashValue(entry, new_lock_ptr); + Ns_RWLockUnlock(&g_locks_mutex); + + return new_lock_ptr; +} + +void +q_delete_lock(void *object_ptr) +{ + Tcl_HashEntry *entry; + /* + * Get a write lock on the locks hashtable, and delete the lock entry. + * Then release the write lock on the locks hashtable. + */ + Ns_RWLockWrLock(&g_locks_mutex); + entry = Tcl_FindHashEntry(&g_locks, object_ptr); + ns_free(Tcl_GetHashValue(entry)); + Tcl_DeleteHashEntry(entry); + Ns_RWLockUnlock(&g_locks_mutex); +} + +void +q_get_rlock(void *object_ptr) +{ + Tcl_HashEntry *entry; +// Ns_Log(Notice,">>>> getting rlock %x",object_ptr); + /* + * Get a read lock on the locks hashtable, and then get the read lock on + * the object lock. + * Then release the read lock on the locks hashtable. + */ + Ns_RWLockRdLock(&g_locks_mutex); + entry = Tcl_FindHashEntry(&g_locks, object_ptr); + Ns_RWLockRdLock(Tcl_GetHashValue(entry)); + Ns_RWLockUnlock(&g_locks_mutex); +// Ns_Log(Notice,">>>> got rlock %x",object_ptr); +} + +void +q_get_wlock(void *object_ptr) +{ + Tcl_HashEntry *entry; +// Ns_Log(Notice,">>>> getting wlock %x",object_ptr); + /* + * Get a read lock on the locks hashtable, and then get the write lock on + * the object lock. + * Then release the read lock on the locks hashtable. + */ + Ns_RWLockRdLock(&g_locks_mutex); + entry = Tcl_FindHashEntry(&g_locks, object_ptr); + Ns_RWLockWrLock(Tcl_GetHashValue(entry)); + Ns_RWLockUnlock(&g_locks_mutex); +// Ns_Log(Notice,">>>> got wlock %x",object_ptr); +} + +void +q_release_lock(void *object_ptr) +{ + Tcl_HashEntry *entry; + /* + * Get a read lock on the locks hashtable, and then release the object lock. + * Then release the read lock on the locks hashtable. + */ + Ns_RWLockRdLock(&g_locks_mutex); + entry = Tcl_FindHashEntry(&g_locks, object_ptr); + Ns_RWLockUnlock(Tcl_GetHashValue(entry)); + Ns_RWLockUnlock(&g_locks_mutex); +} Index: openacs-4/contrib/misc/smsc/qcluster/locks.h =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/locks.h,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/qcluster/locks.h 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,35 @@ +/* + * This file is part of QCluster. + * + * QCluster is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * QCluster is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QCluster; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: locks.h,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +#include "ns.h" + +#ifndef _LOCKS_H +#define _LOCKS_H + +extern void q_init_lock (void); +extern Ns_RWLock * q_add_lock(void *object_ptr); +extern void q_delete_lock(void *object_ptr); +extern void q_get_rlock(void *object_ptr); +extern void q_get_wlock(void *object_ptr); +extern void q_release_lock(void *object_ptr); + +#endif Index: openacs-4/contrib/misc/smsc/qcluster/message.c =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/message.c,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/qcluster/message.c 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,684 @@ +/* + * This file is part of QCluster. + * + * QCluster is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * QCluster is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QCluster; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: message.c,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +static char rcsid[] = "@(#) $Id: message.c,v 1.1 2003/12/02 06:17:38 rmello Exp $"; + +#include "ns.h" +#include "message.h" +#include "clustercomms.h" +#include "locks.h" + +Ns_RWLock g_msg_types_mutex; +Tcl_HashTable g_msg_types; + +static void q_delegate_group(struct q_server *remote_server_ptr, + struct q_group *local_group_ptr); + +/* + *---------------------------------------------------------------------- + * + * q_register_msg_type -- + * + * Registers a particular message type with the qcluster system. This + * function should be used by the layer above qcluster. Two function + * pointers should be provided for encoding and decoding between local + * host and network tranmission. + * + * Results: + * Pointer to the newly created message type object. + * + *---------------------------------------------------------------------- + */ +struct q_message_type * +q_register_msg_type( + int msg_type_id, char *msg_type_name, + int (*construct_in_func)(void *, int, void *, int *), + int (*construct_out_func)(void *, int, void *, int *)) { + struct q_message_type *type_ptr; + Tcl_HashEntry *entry_ptr; + int new_flag; + + /* + * Create the message type object, and add it to the message types + * hash table. + */ + type_ptr = ns_malloc(sizeof(struct q_message_type)); + type_ptr->msg_type_id = msg_type_id; + strcpy(type_ptr->type_name, msg_type_name); + type_ptr->construct_in_func = construct_in_func; + type_ptr->construct_out_func = construct_out_func; + + Ns_RWLockRdLock(&g_msg_types_mutex); + entry_ptr = Tcl_CreateHashEntry(&g_msg_types, + (char *)msg_type_id, + &new_flag); + Tcl_SetHashValue(entry_ptr, type_ptr); + Ns_RWLockUnlock(&g_msg_types_mutex); + + return type_ptr; +} + +/* + *---------------------------------------------------------------------- + * + * q_can_process_p -- + * + * Adds the process_p number to the process_p flag, thus keeping a count + * of the number of processors on this server. + * + * Results: + * None + * + *---------------------------------------------------------------------- + */ +void +q_set_can_process_p(struct q_group *group_ptr, int can_process_p) { + q_get_wlock(group_ptr); + group_ptr->can_process_p += can_process_p; + if (group_ptr->can_process_p < 0) { + can_process_p = 0; + } + /* + * Send a can_process update message to remote servers + */ + q_comms_can_process_msg(group_ptr); + q_release_lock(group_ptr); +} + +/* + *---------------------------------------------------------------------- + * + * q_remote_can_process_p -- + * + * Adds the process_p number to the process_p flag, thus keeping a count + * of the number of processors on the remote server. + * + * Results: + * None + * + *---------------------------------------------------------------------- + */ +void +q_remote_set_can_process_val(struct q_server *remote_server_ptr, + struct q_group *group_ptr, int can_process_val) { + struct q_group *local_group_ptr; + int local_can_process_val; + int local_queue_empty; + q_get_wlock(group_ptr); + group_ptr->can_process_p = can_process_val; + q_release_lock(group_ptr); + + /* + * Check whether the remote server is indicating that it can process + * a group that this server cannot, and that the local group has messages. + * If this is the case, then we need to delegate those messages to the + * remote host. + */ + if (can_process_val > 0) { + local_group_ptr = q_get_group_ptr(g_server, group_ptr->grp_name); + if (local_group_ptr == NULL) { + Ns_Log(Error,"q_remote_set_can_process_val: Cannot find local group %s.", + group_ptr->grp_name); + } else { + q_get_rlock(local_group_ptr); + local_can_process_val = local_group_ptr->can_process_p; + if (local_group_ptr->queue_ll == NULL) { + local_queue_empty = 1; + } else { + local_queue_empty = 0; + } + q_release_lock(local_group_ptr); + if (!local_queue_empty && local_can_process_val == 0) { + /* + * Delegate the local group to the remote server. + */ + q_delegate_group(remote_server_ptr, local_group_ptr); + } + } + } +} + +/* + *---------------------------------------------------------------------- + * + * q_delegate_group -- + * + * Delegates an entire group to a specified remote server. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +void +q_delegate_group(struct q_server *remote_server_ptr, + struct q_group *local_group_ptr) { + Tcl_HashEntry *entry_ptr; + int new_flag; + int delegated_flag; + int can_process_p; + + struct q_message *message_ptr; + struct q_message_search msg_search; + +// Ns_Log(Notice, "q_delegate_group: entering (%s)", local_group_ptr->grp_name); + + + /* + * Loop through each message in the group, attempting to delegate the + * message to the remote_server. If the delegation is successful, delete + * the message locally and send a delete_recovered_message to delete the + * message off the remote queues. + */ + q_get_rlock(local_group_ptr); + message_ptr = q_data_first_msg_entry(local_group_ptr, + -1, + &msg_search); + while (message_ptr != NULL) { +// Ns_Log(Notice,"Delegating message %s", message_ptr->msg_id); + /* + * Delete the message pointer entry from the old group object. + */ + + delegated_flag = q_comms_send_delegate_msg(remote_server_ptr, + message_ptr, g_cluster_size); + + if (delegated_flag == NS_TRUE) { + q_comms_delete_recovered_message(g_server, + message_ptr); + q_data_delete_msg(message_ptr->grp_ptr, + message_ptr->msg_id, + NS_TRUE, NS_TRUE); + } + message_ptr = q_data_next_msg_entry(&msg_search); + } + + q_release_lock(local_group_ptr); + return; +} + + +/* + *---------------------------------------------------------------------- + * + * q_get_group_ptr -- + * + * Returns a pointer to the group object specified by the server + * pointer and group name parameters. + * + * Results: + * Group object pointer if successfully found. + * Null otherwise. + * + *---------------------------------------------------------------------- + */ +struct q_group * +q_get_group_ptr(struct q_server *server_ptr, char *group_name) { + Tcl_HashEntry *entry_ptr; + struct q_group *group_ptr; + + /* + * Try and find the group in this server entry. If found, return a + * pointer to it, otherwise return NULL. + */ + q_get_rlock(server_ptr); + entry_ptr = Tcl_FindHashEntry(&server_ptr->groups_ht, group_name); + q_release_lock(server_ptr); + + if (entry_ptr == NULL) { + group_ptr = NULL; + } else { + group_ptr = Tcl_GetHashValue(entry_ptr); + } + + return group_ptr; +} + + + +/* + *---------------------------------------------------------------------- + * + * q_get_msg_type_ptr -- + * + * Returns a pointer to the message type object, specified by the + * given message type id parameter. + * + * Results: + * Message type object pointer if successfully found. + * Null otherwise. + * + *---------------------------------------------------------------------- + */ +struct q_message_type * +q_get_msg_type_ptr(int msg_type_id) { + Tcl_HashEntry *entry_ptr; + struct q_message_type *msg_type_ptr; + + /* + * Try and find the message_type. If found, return a * pointer to it, + * otherwise return NULL. + */ + Ns_RWLockRdLock(&g_msg_types_mutex); + entry_ptr = Tcl_FindHashEntry(&g_msg_types, (void *)msg_type_id); + Ns_RWLockUnlock(&g_msg_types_mutex); + + if (entry_ptr == NULL) { + msg_type_ptr = NULL; + } else { + msg_type_ptr = Tcl_GetHashValue(entry_ptr); + } + + return msg_type_ptr; +} + + +/* + *---------------------------------------------------------------------- + * + * q_queue_msg -- + * + * Queues a message within the given group. If the "cat_process_p" + * flag is false, it'll pass the message to its nearest neighbour. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +int +q_queue_msg(struct q_message_type *msg_type_ptr, struct q_group *group_ptr, + char *msg_id, int msg_status, void *msg_buf, int msg_size, + int cur_hops) { + struct q_message *msg_ptr; + Tcl_HashEntry *entry_ptr; + int new_flag; + int delegated_flag; + int can_process_p; + +// Ns_Log(Notice, "q_queue_msg: entering (%s) curhops = %d", msg_id, cur_hops); + + /* + * Construct the new message object. + */ + msg_ptr = ns_malloc(sizeof(struct q_message)); + msg_ptr->msg_ptr = ns_malloc(msg_size); + strcpy(msg_ptr->msg_id, msg_id); + memcpy(msg_ptr->msg_ptr, msg_buf, msg_size); + msg_ptr->msg_size = msg_size; + msg_ptr->timestamp = time(NULL); + msg_ptr->status = msg_status; + msg_ptr->grp_ptr = group_ptr; + msg_ptr->type_ptr = msg_type_ptr; + + q_get_rlock(group_ptr); + can_process_p = group_ptr->can_process_p; + q_release_lock(group_ptr); + + delegated_flag = NS_FALSE; + if (!can_process_p) { + /* + * This server can't process this message group, so delegate this + * message to the nearest neighbour. If the delegation operation fails, + * then we have no choice but to queue the message here. + */ + delegated_flag = q_comms_delegate_msg(NULL,msg_ptr, cur_hops); + } + + if (can_process_p || + delegated_flag == NS_FALSE) { + /* + * This server can process this message group, so add the message to + * the group. + */ + q_get_wlock(group_ptr); + q_data_add_msg(group_ptr,msg_ptr); + q_release_lock(group_ptr); + + /* + * Pass a copy of this message to all connected servers. + */ + q_comms_distribute_msg(NULL,msg_ptr); + + /* + * Signal the queue append event for any consuming threads. + */ + Ns_CondBroadcast(&group_ptr->event); + } +// Ns_Log(Notice, "q_queue_msg: exiting (%x)", msg_ptr); + return NS_TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * q_remote_queue_msg -- + * + * Queues a message on another servers queue within the given group. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +int +q_remote_queue_msg(struct q_message_type *msg_type_ptr, + struct q_group *group_ptr, + char *msg_id, int msg_status, + void *msg_buf, int msg_size) { + struct q_message *msg_ptr; + Tcl_HashEntry *entry_ptr; + int new_flag; + int delegated_flag; + int can_process_p; + +// Ns_Log(Notice, "q_queue_remote_msg: entering (%s)", msg_id); + + /* + * Construct the new message object. + */ + msg_ptr = ns_malloc(sizeof(struct q_message)); + msg_ptr->msg_ptr = ns_malloc(msg_size); + strcpy(msg_ptr->msg_id, msg_id); + memcpy(msg_ptr->msg_ptr, msg_buf, msg_size); + msg_ptr->msg_size = msg_size; + msg_ptr->timestamp = time(NULL); + msg_ptr->status = msg_status; + msg_ptr->grp_ptr = group_ptr; + msg_ptr->type_ptr = msg_type_ptr; + + /* + * This server can process this message group, so add the message to + * the group. + */ + q_get_wlock(group_ptr); + q_data_add_msg(group_ptr, msg_ptr); + q_release_lock(group_ptr); + +// Ns_Log(Notice, "q_queue_remote_msg: exiting"); + return NS_TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * q_msg_status_update -- + * + * Update the status of the given message id. If the status is + * Q_MSG_STATUS_COMPLETE/FAILED, the message will be deleted from the + * group. Any updates are forwarded to all the other qcluster + * servers. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +int +q_msg_status_update(char *msg_id, struct q_group *group_ptr, + int new_status) { + struct q_message *msg_ptr; + int old_status; + +// Ns_Log(Notice, "q_msg_status_update: entering"); + + q_get_wlock(group_ptr); + + msg_ptr = q_data_get_msg_entry(group_ptr, msg_id); + if (msg_ptr == NULL) { + Ns_Log(Error, + "q_msg_status_update: Couldn't find message id %s in group %s", + msg_id, group_ptr->grp_name); + q_release_lock(group_ptr); + return NS_FALSE; + } + + if (new_status == Q_MSG_STATUS_COMPLETE || + new_status == Q_MSG_STATUS_FAILED) { + /* + * If the new status is COMPLETE or FAILED, delete it. Note a slight + * fudge in the status field because of the way the + * q_comms_msg_status_update function assuming the msg_ptr is valid and + * has the new status. + */ + old_status = msg_ptr->status; + msg_ptr->status = new_status; + q_comms_msg_status_update(msg_ptr); + msg_ptr->status = old_status; + q_data_delete_msg(group_ptr, msg_id, NS_TRUE, NS_TRUE); + } else { + + /* + * Try and find the message in this group entry, if found, update its + * status to the new value. + */ + q_data_update_msg_status(group_ptr, + msg_ptr, + new_status); + q_comms_msg_status_update(msg_ptr); + } + + q_release_lock(group_ptr); + +// Ns_Log(Notice, "q_msg_status_update: exiting"); + return NS_TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * q_remote_msg_status_update -- + * + * Update the status of the given message id. If the status is + * Q_MSG_STATUS_COMPLETE, the message will be deleted from the + * group. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +int +q_remote_msg_status_update(char *msg_id, struct q_group *group_ptr, + int new_status) { + struct q_message *msg_ptr; + +// Ns_Log(Notice, "q_remote_msg_status_update: entering (%s,%x)", msg_id, new_status); + + q_get_wlock(group_ptr); + + msg_ptr = q_data_get_msg_entry(group_ptr, msg_id); + if (msg_ptr == NULL) { + Ns_Log(Error, + "q_remote_msg_status_update: Couldn't find message id %s in group %s", + msg_id, group_ptr->grp_name); + q_release_lock(group_ptr); + return NS_FALSE; + } + + if (new_status == Q_MSG_STATUS_COMPLETE || + new_status == Q_MSG_STATUS_FAILED) { + /* + * If the new status is COMPLETE, delete it. + */ + q_data_delete_msg(group_ptr, msg_id, NS_TRUE, NS_TRUE); + } else { + + /* + * Update its status to the new value. + */ + + q_data_update_msg_status(group_ptr, + msg_ptr, + new_status); + } + + q_release_lock(group_ptr); + +// Ns_Log(Notice, "q_remote_msg_status_update: exiting"); + return NS_TRUE; +} + + + +/* + *---------------------------------------------------------------------- + * + * q_recover_msg -- + * + * Recovers a message from a failed server. + * + * NB: This function DOES NOT GET LOCKS on the failed server structure + * because it relies on the calling function to get this lock. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ +int +q_recover_msg(struct q_server *failed_server_ptr, + struct q_message *msg_ptr) { + struct q_group *group_ptr; + Tcl_HashEntry *entry_ptr; + int new_flag; + int can_process_p; + int delegated_flag; + int cur_hops; + +// Ns_Log(Notice, "q_recover_msg: entering (%s)", msg_ptr->msg_id); + + /* + * Reuse the message object, but update the group pointer to the local + * server group. + */ + group_ptr = q_get_group_ptr(g_server, msg_ptr->grp_ptr->grp_name); + if (group_ptr == NULL) { + Ns_Log(Error,"q_recover_msg: group name not found: %s", + msg_ptr->grp_ptr->grp_name); + return NS_FALSE; + } + + /* + * Delete the message pointer entry from the old group object. + */ + q_data_delete_msg(msg_ptr->grp_ptr, + msg_ptr->msg_id, + NS_FALSE, + NS_FALSE); + + msg_ptr->grp_ptr = group_ptr; + msg_ptr->status = Q_MSG_STATUS_READY; + + q_get_rlock(group_ptr); + can_process_p = group_ptr->can_process_p; + q_release_lock(group_ptr); + + delegated_flag = NS_FALSE; + if (!can_process_p) { + /* + * This server can't process this message group, so delegate this + * message to the nearest neighbour. If the delegation operation fails, + * then we have no choice but to queue the message here. + */ + delegated_flag = q_comms_delegate_msg(failed_server_ptr, + msg_ptr, g_cluster_size); + } + + if (can_process_p || + delegated_flag == NS_FALSE) { + /* + * This server can process this message group, so add the message to + * the group. + */ + q_get_wlock(group_ptr); + q_data_add_msg(group_ptr, msg_ptr); + q_release_lock(group_ptr); + + /* + * Pass a copy of this message to all connected servers. + */ + q_comms_distribute_msg(failed_server_ptr, msg_ptr); + } +// Ns_Log(Notice, "q_recover_msg: exiting"); + return NS_TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * q_get_msg_and_set_status -- + * + * Retrieves a message with the given status from the given group. The + * status of the message is then updated to the new status in one, single + * atomic operation. + * + * Results: + * Message pointer if a message was found. NULL otherwise. + * + *---------------------------------------------------------------------- + */ +struct q_message * +q_get_msg_and_set_status(struct q_group *group_ptr, + int cur_status, + int new_status) { + struct q_message *msg_ptr; + struct q_message_search search; + int old_status; + +// Ns_Log(Notice, "q_get_msg_and_set_status: entering"); + + q_get_wlock(group_ptr); + + msg_ptr = q_data_first_msg_entry(group_ptr, cur_status, &search); + + if (msg_ptr != NULL) { + if (new_status == Q_MSG_STATUS_COMPLETE || + new_status == Q_MSG_STATUS_FAILED) { + /* + * If the new status is COMPLETE or FAILED, delete it. + */ + old_status = msg_ptr->status; + msg_ptr->status = new_status; + q_comms_msg_status_update(msg_ptr); + msg_ptr->status = old_status; + q_data_delete_msg(group_ptr, msg_ptr->msg_id, NS_TRUE, NS_TRUE); + } else { + /* + * Update the status of the message. + */ + q_data_update_msg_status(group_ptr, + msg_ptr, + new_status); + q_comms_msg_status_update(msg_ptr); + } + } + + q_release_lock(group_ptr); + +// Ns_Log(Notice, "q_get_msg_and_set_status: exiting"); + return msg_ptr; +} + Index: openacs-4/contrib/misc/smsc/qcluster/message.h =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/message.h,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/qcluster/message.h 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,67 @@ +/* + * This file is part of QCluster. + * + * QCluster is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * QCluster is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QCluster; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: message.h,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +#include "ns.h" +#include "cluster.h" + +#ifndef _MESSAGE_H +#define _MESSAGE_H + +extern struct q_message_type * q_register_msg_type( + int msg_type_id, char *msg_type_name, + int (*construct_in_func)(void *, int, void *, int *), + int (*construct_out_func)(void *, int, void *, int *)); +extern void q_set_can_process_p(struct q_group *group_ptr, int can_process_p); +extern void q_remote_set_can_process_val(struct q_server *remote_server_ptr, + struct q_group *group_ptr, int can_process_p); + +extern struct q_group *q_get_group_ptr(struct q_server *server_ptr, + char *group_name); +extern struct q_message_type *q_get_msg_type_ptr(int msg_type_id); +extern int q_queue_msg(struct q_message_type *msg_type_ptr, + struct q_group *group_ptr, + char * msg_id, int msg_status, + void *msg_buf, int msg_size, + int cur_hops); +extern int q_msg_status_update(char *msg_id, struct q_group *group_ptr, + int new_status); +struct q_message *q_get_msg_and_set_status(struct q_group *group_ptr, + int cur_status, + int old_status); + +/* + * Functions used for internal qcluster use. These functions are used + * by the cluster communications functions in clustercomms file. + */ +extern int q_remote_queue_msg(struct q_message_type *msg_type_ptr, + struct q_group *group_ptr, + char * msg_id, int msg_status, + void *msg_buf, int msg_size); +extern int q_recover_msg(struct q_server *failed_server_ptr, + struct q_message *msg_ptr); +extern int q_remote_msg_status_update(char *msg_id, struct q_group *group_ptr, + int new_status); + +extern Ns_RWLock g_msg_types_mutex; +extern Tcl_HashTable g_msg_types; + +#endif Index: openacs-4/contrib/misc/smsc/qcluster/qcluster.c =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/qcluster/qcluster.c,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/qcluster/qcluster.c 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,726 @@ +/* + * This file is part of QCluster. + * + * QCluster is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * QCluster is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with QCluster; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: qcluster.c,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +static char rcsid[] = "@(#) $Id: qcluster.c,v 1.1 2003/12/02 06:17:38 rmello Exp $"; + +#include "ns.h" +#include "cluster.h" +#include "clustercomms.h" +#include "message.h" +#include "locks.h" + +int Ns_ModuleVersion = 1; + +Ns_Mutex g_recover_mutex; + +Ns_Mutex g_send_mutex; + +/* + * Static procedures. + */ +static Ns_SockProc q_AcceptProc; +static void q_ConnectThread(void *arg); +static void q_ServerThread(void *arg); +static int q_compare_server_names(const void *, const void *); +static void q_shutdown(void); + +/* + *---------------------------------------------------------------------- + * + * Ns_ModuleInit -- + * + * Load the config parameters, setup the structures, and + * listen on the control port. + * + * Results: + * None. + * + * Side effects: + * Server will listen for control connections on specified + * address and port. + * + *---------------------------------------------------------------------- + */ + +NS_EXPORT int +Ns_ModuleInit(char *server, char *module) +{ + char tmp_ip_str[Q_MAX_NAME_SIZE]; + char tmp_name_str[Q_MAX_NAME_SIZE]; + int tmp_port; + char *path; + char *pass; + char *key; + char *val; + char *group_name_ptr; + int i; + int count; + int status_count; + int group_id; + int new_flag; + SOCKET lsock; + Ns_Set *set; + Tcl_HashEntry *entry_ptr; + Tcl_HashEntry *search_ptr; + Tcl_HashTable groups; + Tcl_HashSearch search; + struct q_server* server_ptr; + struct q_group* group_ptr; + + Ns_Log(Notice, "qcluster: initialising"); + Ns_MutexInit(&g_send_mutex); + + /* + * Initialise the shared memory (locks) module and any global locks + * and hash tables. + */ + Tcl_InitHashTable(&g_msg_types, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(&g_cluster, TCL_STRING_KEYS); + Ns_RWLockInit(&g_msg_types_mutex); + Ns_RWLockInit(&g_cluster_mutex); + Ns_MutexInit(&g_recover_mutex); + q_init_lock(); + + /* + * Read the default values for local address and port for the + * listener socket. Note that these values may be overridden by + * an entry for this server in the cluster entries that specifies + * a specific port. + */ + path = Ns_ConfigGetPath(server, module, NULL); + + if ( ((g_addr = Ns_ConfigGet(path, "address")) == NULL) + || (!Ns_ConfigGetInt(path, "port", &g_port)) + || ((g_iam = Ns_ConfigGet(path, "iam")) == NULL) ) { + Ns_Log(Error, + "qcluster: address must be specified in config"); + return NS_ERROR; + } + + /* + * Read the groups from the config file. + */ + Tcl_InitHashTable(&groups, TCL_STRING_KEYS); + path = Ns_ConfigGetPath(server, module, "groups", NULL); + set = Ns_ConfigGetSection(path); + group_id = 0; + for (i = 0; set != NULL && i < Ns_SetSize(set); ++i) { + pass = NULL; + key = Ns_SetKey(set, i); + group_name_ptr = Ns_SetValue(set, i); + + if (!STRIEQ(key, "group")) { + Ns_Log(Error, "qcluster: invalid groups key: %s", key); + continue; + } else { + /* + * Add the group entry to the hash table. + * lock for the structure in the locks table. + */ + entry_ptr = Tcl_CreateHashEntry(&groups, group_name_ptr, + &new_flag); + Tcl_SetHashValue(entry_ptr, (void *) group_id); + group_id++; + } /* End "if key = group" else. */ + } + if (groups.numEntries == 0) { + Ns_Log(Warning, "qcluster: No groups specified"); + } + + /* + * Read the list of cluster members. Initialise the hash table for + * each member found. + */ + g_server = NULL; + path = Ns_ConfigGetPath(server, module, "cluster", NULL); + set = Ns_ConfigGetSection(path); + g_cluster_size = 0; + for (i = 0; set != NULL && i < Ns_SetSize(set); ++i) { + pass = NULL; + key = Ns_SetKey(set, i); + val = Ns_SetValue(set, i); + + if (!STRIEQ(key, "member")) { + Ns_Log(Error, "qcluster: invalid cluster key: %s", key); + continue; + } else { + count = sscanf(val, "%[^:]:%[^:]:%d", tmp_ip_str, + tmp_name_str, + &tmp_port); + if (count == 0) { + Ns_Log(Error, "qcluster: invalid cluster value: %s", key); + Ns_Log(Error, "qcluster: ipaddress[:name][:port]"); + continue; + } + /* + * Create structure for this host. + */ + server_ptr = ns_malloc(sizeof(struct q_server)); + strcpy(server_ptr->svr_ip, tmp_ip_str); + Ns_MutexInit(&server_ptr->c_mutex); + Ns_MutexInit(&server_ptr->s_mutex); + server_ptr->c_sock = -1; + server_ptr->s_sock = -1; + + /* + * Add this server to the array of servers. + */ + g_cluster_order[g_cluster_size] = server_ptr; + g_cluster_size++; + + /* + * If a server name wasn't given for this entry, just use the + * textual version of the IP address. Otherwise, copy in the + * given name. + * + * If a server port wasn't given for this entry, use the default + * port as specified in the main qcluster section (read above). + */ + if (count > 1) { + strcpy(server_ptr->svr_name, tmp_name_str); + } else { + strcpy(server_ptr->svr_name, tmp_ip_str); + } + + if (count > 2) { + server_ptr->l_port = tmp_port; + } else { + server_ptr->l_port = g_port; + } + + /* + * Check whether the server name matches this one. If so, + * set the flag to indicate that this server structure corresponds + * to us. Also, reset the listening port for this instance (just + * in case the specified port in this server's entry is different to + * the default). + */ + if (!strcmp(server_ptr->svr_name, g_iam)) { + server_ptr->iam_p = 1; + g_port = server_ptr->l_port; + g_server = server_ptr; + } else { + server_ptr->iam_p = 0; + } + + /* + * Loop through each configured group name and initialise the + * groups hash table for this server. + */ + Tcl_InitHashTable(&server_ptr->groups_ht, TCL_STRING_KEYS); + search_ptr = Tcl_FirstHashEntry(&groups, &search); + while (search_ptr != NULL) { + group_name_ptr = Tcl_GetHashKey(&groups, search_ptr); + /* + * Create the new group structure, initialise its values, and + * add it to the servers groups hash table. + */ + group_ptr = ns_malloc(sizeof(struct q_group)); + strcpy(group_ptr->grp_name, group_name_ptr); + group_ptr->grp_id = (int) Tcl_GetHashValue(search_ptr); + group_ptr->can_process_p = 0; + Ns_CondInit(&group_ptr->event); + Ns_MutexInit(&group_ptr->event_lock); + Tcl_InitHashTable(&group_ptr->queue_ht, TCL_STRING_KEYS); + group_ptr->queue_ll = NULL; + for (status_count = 0; + status_count < Q_MSG_STATUS_MAX; + status_count++) { + group_ptr->queue_by_stat_ll[status_count] = NULL; + } + entry_ptr = Tcl_CreateHashEntry(&server_ptr->groups_ht, + group_name_ptr, + &new_flag); + Tcl_SetHashValue(entry_ptr, group_ptr); + q_add_lock(group_ptr); + search_ptr = Tcl_NextHashEntry(&search); + } + + /* + * Add the new server entry into the hash table, and create the + * lock for the structure in the locks table. + */ + entry_ptr = Tcl_CreateHashEntry(&g_cluster, server_ptr->svr_name, + &new_flag); + Tcl_SetHashValue(entry_ptr, server_ptr); + q_add_lock(server_ptr); + Ns_Log(Notice, "qcluster: added host: %s", server_ptr->svr_name); + } /* End "if key = member" else. */ + } + if (g_cluster.numEntries == 0) { + Ns_Log(Warning, "qcluster: No cluster members"); + } + + /* + * Check that the iam server entry existed in the cluster list. + */ + if (g_server == NULL) { + Ns_Log(Error, + "qcluster: iam server name must exist in the cluster list. Please check you're configuration."); + return NS_ERROR; + } + + /* + * Sort the server array such that we know who our nearest neighbour is. + */ + qsort(g_cluster_order, g_cluster_size, sizeof(struct q_server *), + q_compare_server_names); + + /* + * Work out where we are in the cluster list and set the g_this_server_pos + * global variable. + */ + for (i = 0; i < g_cluster_size; i++) { + if (g_cluster_order[i] == g_server) { + g_this_server_pos = i; + } + } + + /* + * Start the listening socket. + */ + lsock = Ns_SockListen(g_addr, g_port); + if (lsock == INVALID_SOCKET) { + Ns_Log(Error, "qcluster: could not listen on %s:%d", g_addr, g_port); + return NS_ERROR; + } + Ns_Log(Notice, "qcluster: listening on %s:%d", g_addr, g_port); + +// Don't know what this does!? +// Ns_RegisterProcInfo(AcceptProc, "qcluster", ArgProc); + + Ns_SockCallback(lsock, q_AcceptProc, + g_server, NS_SOCK_READ|NS_SOCK_EXIT); + + /* + * Loop through each server entry, and assuming the entry doesn't + * correspond to this server, start a thread to attempt and manage + * the connection to the remote host. + */ + Ns_RWLockRdLock(&g_cluster_mutex); + search_ptr = Tcl_FirstHashEntry(&g_cluster, &search); + while (search_ptr != NULL) { + server_ptr = Tcl_GetHashValue(search_ptr); + + q_get_rlock(server_ptr); + if (!server_ptr->iam_p) { + Ns_ThreadCreate(q_ConnectThread, (void *) server_ptr, 0, NULL); + } + q_release_lock(server_ptr); + search_ptr = Tcl_NextHashEntry(&search); + } + Ns_RWLockUnlock(&g_cluster_mutex); + + g_qcluster_started = 1; + return NS_OK; +} + +static int +q_compare_server_names(const void *ptr1, const void *ptr2) { + return strcmp((*(struct q_server **)ptr1)->svr_name, + (*(struct q_server **)ptr2)->svr_name); +} + +int server_recv(int sock, void *msg_buf, int msg_size, + int flags) +{ + int read_len; + int read_val; + read_len = 0; + read_val = 0; + + while (read_len < msg_size) { + if ((read_val = recv(sock, + msg_buf + read_len, + msg_size - read_len, flags)) <= 0) { + read_len = -1; + break; + } else { + read_len += read_val; + } + } + return read_len; +} + +/* + *---------------------------------------------------------------------- + * + * q_AcceptProc -- + * + * Socket callback to accept a new connection. + * + * Results: + * NS_TRUE to keep listening unless shutdown is in progress. + * + * Side effects: + * New EvalThread will be created. + * + *---------------------------------------------------------------------- + */ + +static int +q_AcceptProc(SOCKET lsock, void *object_ptr, int why) +{ + int n; + int len; + int s_sock; + static int next; + struct q_server *this_server_ptr; + struct q_server *named_server_ptr; + struct sockaddr sa; + int msg_header[2]; + int msg_id; + int msg_size; + char server_name[Q_MAX_NAME_SIZE]; + Tcl_HashEntry *entry; + + this_server_ptr = (struct q_server *)object_ptr; + + if (why == NS_SOCK_EXIT) { + Ns_Log(Notice, "qcluster: shutdown"); + q_shutdown(); + ns_sockclose(lsock); + return NS_FALSE; + } + + len = sizeof(sa); + s_sock = Ns_SockAccept(lsock, + &sa, + &len); + if (s_sock == INVALID_SOCKET) { + Ns_Log(Error, "qcluster: accept() failed: %s, %d", + ns_sockstrerror(ns_sockerrno), ns_sockerrno); + } else { + /* + * Read header of identification message: + * 4 octets: Message Id + * 4 octets: Message Size (excluding header) + */ + if ((n = server_recv(s_sock, msg_header, sizeof(msg_header), 0)) <= 0) { + Ns_Log(Error, "qcluster: header receive error: %s", + ns_sockstrerror(ns_sockerrno)); + ns_sockclose(s_sock); + goto error_exit; + } + msg_id = ntohl(msg_header[0]); + msg_size = ntohl(msg_header[1]); + + if (DEBUG) { + Ns_Log(Notice, "qcluster: header received (%d, %d)", + msg_id, msg_size); + } + + /* + * Check that we've received the correct header format. + */ + if (msg_id != Q_MSG_TYPE_ID) { + Ns_Log(Error, "qcluster: Incorrect header message id: %d", + msg_id); + ns_sockclose(s_sock); + goto error_exit; + } + + /* + * Receive the name of the remote connection. Remeber to terminate + * the localy stored copy with a zero. + */ + if ((n = server_recv(s_sock, server_name, msg_size, 0)) <= 0) { + Ns_Log(Error, "qcluster: server identifier message receive error: %s", + ns_sockstrerror(ns_sockerrno)); + ns_sockclose(s_sock); + goto error_exit; + } + server_name[msg_size] = '\0'; + + /* + * Try and find this server in the cluster hashtable. Once found, + * Update the connection details and kick off a server thread to + * service this connection. + */ + Ns_RWLockRdLock(&g_cluster_mutex); + entry = Tcl_FindHashEntry(&g_cluster, server_name); + Ns_RWLockUnlock(&g_cluster_mutex); + + if (entry == NULL) { + Ns_Log(Error, "qcluster: server_name %s unknown", + server_name); + ns_sockclose(s_sock); + goto error_exit; + } + named_server_ptr = Tcl_GetHashValue(entry); + + q_get_wlock(named_server_ptr); + if (named_server_ptr->iam_p) { + q_release_lock(named_server_ptr); + Ns_Log(Error, "qcluster: Cannot accept connections from oneself!"); + ns_sockclose(s_sock); + goto error_exit; + } + named_server_ptr->s_sock = s_sock; + memcpy(&named_server_ptr->s_sa, + &sa, + sizeof(sa)); + q_release_lock(named_server_ptr); + Ns_ThreadCreate(q_ServerThread, (void *) named_server_ptr, 0, NULL); + } + + error_exit: + + return NS_TRUE; +} + + + +/* + *---------------------------------------------------------------------- + * + * q_shutdown -- + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +q_shutdown(void) +{ + int count; + struct q_server *server_ptr; + + for (count = 0; count < g_cluster_size; count++) { + server_ptr = g_cluster_order[count]; + if (server_ptr != g_server) { + q_get_rlock(server_ptr); + ns_sockclose(server_ptr->c_sock); + ns_sockclose(server_ptr->s_sock); + q_release_lock(server_ptr); + } + } +} + +/* + *---------------------------------------------------------------------- + * + * q_ConnectThread -- + * + * Thread to connect to remote host. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +q_ConnectThread(void *arg) +{ + struct q_server *server_ptr; + int port; + char svr_name[Q_MAX_NAME_SIZE]; + char svr_ip[Q_MAX_NAME_SIZE]; + char this_svr_name[Q_MAX_NAME_SIZE]; + int sock; + int n; + int msg_header[2]; + int msg_id; + int msg_size; + char msg_buf[Q_MAX_MSG_SIZE]; + Ns_Mutex *c_mutex; + fd_set set; + SOCKET sock_pipe[2]; + int write_len; + int max; + unsigned char dummy_byte; + + + server_ptr = (struct q_server *) arg; + + /* + * Log the starting of this thread, and make thread local copies of the + * remote server's IP address and listening port. + */ + q_get_rlock(server_ptr); + Ns_Log(Notice, "qcluster: client connect thread starting for %s", + server_ptr->svr_name); + port = server_ptr->l_port; + strcpy(svr_ip, server_ptr->svr_ip); + strcpy(svr_name, server_ptr->svr_name); + c_mutex = &server_ptr->c_mutex; + q_release_lock(server_ptr); + q_get_rlock(g_server); + strcpy(this_svr_name, g_server->svr_name); + q_release_lock(g_server); + + while (1) { +// Ns_Log(Notice, "connect: Waiting for %s lock", server_ptr->svr_name); + Ns_MutexLock(c_mutex); +// Ns_Log(Notice, "connect: Got for %s lock", server_ptr->svr_name); + sock = Ns_SockTimedConnect(svr_ip, port, 5); + if (sock == INVALID_SOCKET) { + Ns_Log(Notice, "qcluster: Connection to %s:%s:%d failed.", + svr_ip, svr_name, port); + Ns_MutexUnlock(c_mutex); + sleep(1); + continue; + } + /* + * Use the connected socket. + */ + Ns_Log(Notice, "qcluster: Connection to %s:%s:%d succeeded. (%x->%d)", + svr_ip, svr_name, port, server_ptr, sock); + q_get_rlock(server_ptr); + server_ptr->c_sock = sock; + q_release_lock(server_ptr); + + /* + * Send the header identification message. + */ + msg_header[0] = htonl(Q_MSG_TYPE_ID); + msg_header[1] = htonl(strlen(this_svr_name)); + send(sock, msg_header, sizeof(msg_header), 0); + send(sock, this_svr_name, strlen(this_svr_name), 0); + + Ns_MutexUnlock(c_mutex); + + /* + * Dump copies of our message queues to the newly attached host. + */ + q_comms_server_connect_dump_messages(server_ptr); + + /* + * Loop whilst still connected. + */ + + while (1) { + + if ((n = server_recv(sock, msg_header, sizeof(msg_header), 0)) <= 0) { + Ns_Log(Error, "qcluster: header receive error: %s", + ns_sockstrerror(ns_sockerrno)); + ns_sockclose(sock); + break; + } + msg_id = ntohl(msg_header[0]); + msg_size = ntohl(msg_header[1]); + + if ((n = server_recv(sock, msg_buf, msg_size, 0)) <= 0) { + Ns_Log(Error, "qcluster: message content receive error: %s", + ns_sockstrerror(ns_sockerrno)); + ns_sockclose(sock); + break; + } + q_comms_process(server_ptr, msg_id, msg_buf, msg_size); + } + + q_get_wlock(server_ptr); + server_ptr->c_sock = -1; +// ns_sockclose(server_ptr->s_sock); +// server_ptr->s_sock = -1; + q_release_lock(server_ptr); + Ns_Log(Error, "qcluster: Connection to host %s terminated", + svr_name); + /* + * Recover the server, getting the lock such that we only recover + * one server at a time. This prevents any lock contentions during + * the recover process. + */ + Ns_MutexLock(&g_recover_mutex); + q_comms_recover_server(server_ptr); + Ns_MutexUnlock(&g_recover_mutex); + + Ns_Log(Notice, "connect: Recovering complete for %s", server_ptr->svr_name); + } + +} + + +/* + *---------------------------------------------------------------------- + * + * q_ServerThread -- + * + * Thread to handle incoming connection from remote host. + * + * Results: + * None. + * + *---------------------------------------------------------------------- + */ + +static void +q_ServerThread(void *arg) +{ + struct q_server *server_ptr; + char svr_name[Q_MAX_NAME_SIZE]; + char this_svr_name[Q_MAX_NAME_SIZE]; + int sock; + int n; + int msg_header[2]; + int msg_id; + int msg_size; + char msg_buf[Q_MAX_MSG_SIZE]; + + server_ptr = (struct q_server *) arg; + + /* + * Log the starting of this thread, and make thread local copies of the + * remote server's IP address and listening port. + */ + q_get_rlock(server_ptr); + Ns_Log(Notice, "qcluster: server connect thread starting for %s", + server_ptr->svr_name); + strcpy(svr_name, server_ptr->svr_name); + sock = server_ptr->s_sock; + q_release_lock(server_ptr); + q_get_rlock(g_server); + strcpy(this_svr_name, g_server->svr_name); + q_release_lock(g_server); + + while (1) { + if ((n = server_recv(sock, msg_header, sizeof(msg_header), 0)) <= 0) { + Ns_Log(Error, "qcluster: header receive error: %s", + ns_sockstrerror(ns_sockerrno)); + ns_sockclose(sock); + break; + } + msg_id = ntohl(msg_header[0]); + msg_size = ntohl(msg_header[1]); + + if ((n = server_recv(sock, msg_buf, msg_size, 0)) <= 0) { + Ns_Log(Error, "qcluster: message content receive error: %s", + ns_sockstrerror(ns_sockerrno)); + ns_sockclose(sock); + break; + } + q_comms_process(server_ptr, msg_id, msg_buf, msg_size); + } + + q_get_wlock(server_ptr); + server_ptr->s_sock = -1; +// ns_sockclose(server_ptr->c_sock); +// server_ptr->c_sock = -1; + q_release_lock(server_ptr); + Ns_Log(Error, "qcluster: Connection from host %s terminated", + svr_name); + Ns_ThreadExit (0); +} + Index: openacs-4/contrib/misc/smsc/smsq/Makefile =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/smsq/Makefile,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/smsq/Makefile 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,77 @@ +# @(#) $Header: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/smsq/Makefile,v 1.1 2003/12/02 06:17:38 rmello Exp $ +# + +ifdef INST +NSHOME ?= $(INST) +else +NSHOME ?= ../aolserver +endif + +module = smsq +cvspath = nsd-modules/$(module) +version_ = $(subst .,_,$(version)) +distdir = $(module)-$(version) +distfile = $(distdir).tar.gz + +MOD = smsq.so + +# +# Set the objects to build +# + +OBJS = smsq.o gsm0338.o gsm0340.o + +include $(NSHOME)/include/Makefile.global + +all: $(MOD) + +%.o: %.c + $(CC) -c $(CFLAGS) -D_TCL82 $< -o $@ + +$(MOD): $(OBJS) + $(RM) $@ + $(LDSO) $(LDSOFLAGS) -o $@ $^ $(MODLIBS) + +install: all + $(RM) $(INSTBIN)/$(MOD) + $(CP) $(MOD) $(INSTBIN) + +clean: + $(RM) $(MOD) $(OBJS) + +clobber: clean + $(RM) *.so *.o *.a *~ + +distclean: clobber + $(RM) TAGS tags core *.gz + +release: check-version-var + cvs rtag -r stable release-$(version_) $(cvspath) + +force-release: check-version-var + cvs rtag -F -r stable release-$(version_) $(cvspath) + +dist: check-version-var $(distfile) + +publish: dist + scp "$(distfile)" open-msg.com:www/aolserver + ssh open-msg.com 'cd www/aolserver/src && rm -rf "./$(module)-"* && tar xvzf "../$(distfile)"' + ssh -t open-msg.com vi www/aolserver/index.html + +$(distfile): + rm -rf work + mkdir work + cd work && cvs -Q export -r "release-$(version_)" \ + -d "$(distdir)" "$(cvspath)" + find work -type f | xargs perl -pi -e 's/\@VER\@/$(version)/g' + ( cd work && tar cvf - "$(distdir)" ) | gzip -9 > "$(distfile)" + rm -rf work + +.PHONY: check-version-var + +check-version-var: + @if [ "$(version)" = "" ]; then \ + echo 1>&2 "\$$version must be set to version number."; \ + exit 1; \ + fi + Index: openacs-4/contrib/misc/smsc/smsq/gpl.txt =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/smsq/gpl.txt,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/smsq/gpl.txt 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. Index: openacs-4/contrib/misc/smsc/smsq/gsm0338.c =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/smsq/gsm0338.c,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/smsq/gsm0338.c 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,224 @@ +/* + * + * This file is part of Smsq. + * + * Smsq is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Smsq is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Smsq; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: gsm0338.c,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +static char rcsid[] = "@(#) $Id: gsm0338.c,v 1.1 2003/12/02 06:17:38 rmello Exp $"; + +#include "ns.h" + +/* + * + * 8bit to 7bit conversion table. + * + * 00 01 02 03 04 05 06 07 + * 08 09 0a 0b 0c 0d 0e 0f + * 00 - 0f + */ +int g_gsm0338_smsq_8to7[256] = + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x1b0a,0x0d, 0x00, 0x00, +// 10 - 1f + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 20 - 2f + 0x20, 0x21, 0x22, 0x23, 0x02, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, +// 30 - 3f + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, + 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, +// 40 - 4f + 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, +// 50 - 5f + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5a, 0x1b3c,0x1b2f,0x1b3e,0x1b14,0x11, +// 60 - 6f + 0x00, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, +// 70 - 7f + 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x1b28,0x1b40,0x1b29,0x1b3d,0x00, + +// 00, 01, 02, 03, 04, 05, 06, 07, +// 08, 09, 0a, 0b, 0c, 0d, 0e, 0f, +// 80 - 8f + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// 90 - 9f + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// a0 - af +// Note, difference here between 0xa4 as 1b65 (euro sign) - ISO-8859-15 +// and 0xa4 as 24 (currency sign) - ISO-8859-1 + 0x00, 0x40, 0x00, 0x01, 0x1b65,0x03, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +// b0 - bf + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, +// c0 - cf + 0x00, 0x00, 0x00, 0x00, 0x5b, 0x0e, 0x1c, 0x09, + 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, +// d0 - df + 0x00, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x1e, +// e0 - ef + 0x7f, 0x00, 0x00, 0x00, 0x7b, 0x0f, 0x1d, 0x00, + 0x04, 0x05, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, +// f0 - ff + 0x00, 0x7d, 0x08, 0x00, 0x00, 0x00, 0x7c, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x7e, 0x00, 0x00, 0x00}; + +int g_gsm0338_smsq_7to8[128]; + + +/* + * Gsm 7bit to 8bit. + * Fudge the anomoly between ISO-8859-15 and ISO-8859-1 (see above). + */ +int gsm0338_init(void) { + int i; + + for (i = 0; i < 128; i++) { + g_gsm0338_smsq_7to8[i] = 32; + } + + for (i = 0; i < 256; i++) { + if (g_gsm0338_smsq_8to7[i] <= 0x7f) { + g_gsm0338_smsq_7to8[g_gsm0338_smsq_8to7[i]] = i; + } + } +/* + * Fudge the anomoly between ISO-8859-15 and ISO-8859-1 (see above). + */ + g_gsm0338_smsq_7to8[0x24] = 0xa4; + return NS_FALSE; +} + +void +gsm0338_pack_7bit(unsigned char *bits7_ptr, int *bits7_len_ptr, + char *bits8_ptr) { + int i; + int total_chars; + int bits8_len; + unsigned char working_byte; + int cur_char; + int extra_char; + int bottom_bits, top_bits; + int bottom_mask, top_mask; + + bits8_len = strlen(bits8_ptr); + *bits7_len_ptr = 0; + + working_byte = 0; + total_chars = 0; + for (i = 0; i < bits8_len; i++) { + cur_char = g_gsm0338_smsq_8to7[(int)bits8_ptr[i]]; + extra_char = -1; + if (cur_char > 0xff) { + cur_char = (cur_char & 0xff00) >> 8; + extra_char = (cur_char & 0xff); + } + bottom_bits = total_chars % 8; + top_bits = 8 - bottom_bits; + bottom_mask = (1 << bottom_bits) - 1; + top_mask = 0xff - bottom_mask; + + working_byte = ((bottom_mask & cur_char) << top_bits) | working_byte; + if (bottom_bits > 0) { + bits7_ptr[*bits7_len_ptr] = working_byte; + (*bits7_len_ptr)++; + working_byte = 0; + } + working_byte = ((top_mask & cur_char) >> bottom_bits) | working_byte; + total_chars++; + + if (extra_char != -1) { + cur_char = extra_char; + bottom_bits = total_chars % 8; + top_bits = 8 - bottom_bits; + bottom_mask = (1 << bottom_bits) - 1; + top_mask = 0xff - bottom_mask; + + working_byte = ((bottom_mask & cur_char) << top_bits) | working_byte; + if (bottom_bits > 0) { + bits7_ptr[*bits7_len_ptr] = working_byte; + (*bits7_len_ptr)++; + working_byte = 0; + } + working_byte = ((top_mask & cur_char) >> bottom_bits) | working_byte; + total_chars++; + } + } + /* + * If there's anything in the final octet, add it. + */ + if (top_bits > 0) { + bits7_ptr[*bits7_len_ptr] = working_byte; + (*bits7_len_ptr)++; + } + *(bits7_len_ptr) = (bits8_len * 7) / 8; + if ( ((bits8_len * 7) % 8) != 0 ) { + (*bits7_len_ptr)++; + } +} + + +void +gsm0338_unpack_7bit(char *bits8_ptr, + unsigned char *bits7_ptr, int bits7_len, + int unpacked_data_len) { + int i; + int bits8_len; + int carry; + int bottom_bits; + int bottom_mask; + int top_mask; + int cur_char; + char tmp[9]; + + bits8_len = 0; + carry = 0; + bottom_bits = 7; + + for (i = 0; i < bits7_len; i++) { + bottom_mask = (1 << bottom_bits) - 1; + top_mask = 0xff - bottom_mask; + + cur_char = carry + ((bits7_ptr[i] & bottom_mask) << (7 - bottom_bits)); + carry = (bits7_ptr[i] & top_mask) >> bottom_bits; + bits8_ptr[bits8_len] = g_gsm0338_smsq_7to8[cur_char]; + bits8_len++; + bottom_bits--; +// if (bottom_bits == 0 && +// i != (bits7_len - 1)) { + if (bottom_bits == 0) { + bottom_bits = 7; + cur_char = carry; + bits8_ptr[bits8_len] = g_gsm0338_smsq_7to8[cur_char]; + bits8_len++; + carry = 0; + } + } + bits8_len = unpacked_data_len; + bits8_ptr[bits8_len] = '\0'; +} + Index: openacs-4/contrib/misc/smsc/smsq/gsm0338.h =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/smsq/gsm0338.h,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/smsq/gsm0338.h 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,33 @@ +/* + * This file is part of Smsq. + * + * Smsq is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Smsq is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Smsq; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * peter.harper@open-msg.com + * + * $Id: gsm0338.h,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ +extern int g_gsm0338_smsq_8to7[256]; +extern int g_gsm0338_smsq_7to8[128]; + +extern int +gsm0338_init(void); +extern void +gsm0338_pack_7bit(unsigned char *bits7_ptr, int *bits7_len_ptr, + char *bits8_ptr); +extern void +gsm0338_unpack_7bit(char *bits8_ptr, + unsigned char *bits7_ptr, int bits7_len, + int unpacked_data_len); Index: openacs-4/contrib/misc/smsc/smsq/gsm0340.c =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/smsq/gsm0340.c,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/smsq/gsm0340.c 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,32 @@ +/* + * This file is part of Smsq. + * + * Smsq is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Smsq is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Smsq; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: gsm0340.c,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +static char rcsid[] = "@(#) $Id: gsm0340.c,v 1.1 2003/12/02 06:17:38 rmello Exp $"; + +#include "ns.h" + +/* + * + */ +int gsm0340_init(void) { + return NS_FALSE; +} Index: openacs-4/contrib/misc/smsc/smsq/gsm0340.h =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/smsq/gsm0340.h,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/smsq/gsm0340.h 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,21 @@ +/* + * This file is part of Smsq. + * + * Smsq is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Smsq is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Smsq; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: gsm0340.h,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ Index: openacs-4/contrib/misc/smsc/smsq/smsq.c =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/smsq/smsq.c,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/smsq/smsq.c 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,1308 @@ +/* + * This file is part of Smsq. + * + * Smsq is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Smsq is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Smsq; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: smsq.c,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +static char rcsid[] = "@(#) $Id: smsq.c,v 1.1 2003/12/02 06:17:38 rmello Exp $"; + +#include "ns.h" +#include "../qcluster/message.h" +#include "../qcluster/cluster.h" +#include "../qcluster/locks.h" +#include "smsq.h" +#include "gsm0338.h" + +int Ns_ModuleVersion = 1; + +static struct q_message_type *g_sms_submit_type; +static char g_aolserver_name[128]; + +#define SMSQ_ALIGNED_SIZE(s) (s + (sizeof(int) - (s % sizeof(int)))) + +/* + * Static functions + */ +static int smsq_construct_sms_in(void *in_buf, int in_size, + void *out_buf, int *out_size); +static int smsq_construct_sms_out(void *in_buf, int in_size, + void *out_buf, int *out_size); +static int smsq_tcl_submit_ascii(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int smsq_tcl_wait_for_group_activity(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int smsq_tcl_get_next_group_msg(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int smsq_tcl_get_group_names(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int smsq_tcl_get_server_name(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int smsq_tcl_set_group_process_flag(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int smsq_tcl_set_msg_status(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int smsq_tcl_retrieve_ascii(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int smsq_tcl_get_queue_info(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]); +static int smsq_add_commands(Tcl_Interp *interp, ClientData data); +static int smsq_execute_tcl_response(struct q_message *message_ptr, + int success_p); + +/* + *---------------------------------------------------------------------- + * + * Ns_ModuleInit -- + * + * Register the SMS message type with the qcluster module. + * + * Results: + * None. + * + * Side effects: + * + *---------------------------------------------------------------------- + */ + +NS_EXPORT int +Ns_ModuleInit(char *server, char *module) +{ + /* + * Wait for the qcluster module to start. + */ + while (1) { + if (g_qcluster_started) { + break; + } + sleep(1); + } + Ns_Log(Notice, "smsq: initialising"); + (void) gsm0338_init(); + g_sms_submit_type = q_register_msg_type(SMSQ_SUBMIT_TYPE_ID, + SMSQ_SUBMIT_TYPE_NAME, + smsq_construct_sms_in, + smsq_construct_sms_out); + strcpy(g_aolserver_name, server); + Ns_Log(Notice, "smsq: initialisation complete"); + return Ns_TclInitInterps(server, smsq_add_commands, NULL); +} + + +/* + *---------------------------------------------------------------------- + * + * smsq_construct_sms_out -- + * Convert sms message payload for transmission on a network. + * + * Results: + * NS_TRUE - Conversion okay. + * NS_FALSE - Conversion failed + * + *---------------------------------------------------------------------- + */ +int +smsq_construct_sms_out(void *in_buf, int in_size, + void *out_buf, int *out_size) { + struct smsq_sms_submit *in_ptr; + unsigned char *msg_buf; + int msg_size; + + in_ptr = (struct smsq_sms_submit *) in_buf; + msg_buf = (unsigned char *)out_buf; + + msg_size = 0; + + /* type_of_number */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->type_of_number); + msg_size+= sizeof(int); + /* numbering_plan_id */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->numbering_plan_id); + msg_size+= sizeof(int); + /* dest_address */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(in_ptr->dest_address)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], in_ptr->dest_address); + msg_size+= SMSQ_ALIGNED_SIZE(strlen(in_ptr->dest_address)); + /* protocol_id */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->protocol_id); + msg_size+= sizeof(int); + /* data_coding_scheme */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->data_coding_scheme); + msg_size+= sizeof(int); + /* validity_period / tp_mask */ + *((char *)&msg_buf[msg_size ]) = in_ptr->validity_period; + *((char *)&msg_buf[msg_size+1]) = in_ptr->tp_mask; + msg_size+= sizeof(int); + /* safe_stored_p */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->safe_stored_p); + msg_size+= sizeof(int); + /* user_data_len */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->user_data_len); + msg_size+= sizeof(int); + /* user_data */ + memcpy(&msg_buf[msg_size], in_ptr->user_data, in_ptr->user_data_len); + msg_size+= SMSQ_ALIGNED_SIZE(in_ptr->user_data_len); + /* unpacked_data_len */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->unpacked_data_len); + msg_size+= sizeof(int); + + /* timestamp.tm_sec */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_sec); + msg_size+= sizeof(int); + /* timestamp.tm_min */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_min); + msg_size+= sizeof(int); + /* timestamp.tm_hour */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_hour); + msg_size+= sizeof(int); + /* timestamp.tm_mday */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_mday); + msg_size+= sizeof(int); + /* timestamp.tm_mon */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_mon); + msg_size+= sizeof(int); + /* timestamp.tm_year */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->timestamp.tm_year); + msg_size+= sizeof(int); + + /* smsc_timestamp.tm_sec */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_sec); + msg_size+= sizeof(int); + /* smsc_timestamp.tm_min */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_min); + msg_size+= sizeof(int); + /* smsc_timestamp.tm_hour */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_hour); + msg_size+= sizeof(int); + /* smsc_timestamp.tm_mday */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_mday); + msg_size+= sizeof(int); + /* smsc_timestamp.tm_mon */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_mon); + msg_size+= sizeof(int); + /* smsc_timestamp.tm_year */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_year); + msg_size+= sizeof(int); + /* smsc_timestamp.tm_tz */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->smsc_timestamp.tm_tz); + msg_size+= sizeof(int); + + /* success_callback_p */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->success_callback_p); + msg_size+= sizeof(int); + if (in_ptr->success_callback_p) { + /* success_callback */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(in_ptr->success_callback)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], in_ptr->success_callback); + msg_size+= SMSQ_ALIGNED_SIZE(strlen(in_ptr->success_callback)); + } + /* fail_callback_p */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->fail_callback_p); + msg_size+= sizeof(int); + if (in_ptr->fail_callback_p) { + /* fail_callback */ + *((int *)&msg_buf[msg_size]) = htonl(strlen(in_ptr->fail_callback)); + msg_size+= sizeof(int); + strcpy(&msg_buf[msg_size], in_ptr->fail_callback); + msg_size+= SMSQ_ALIGNED_SIZE(strlen(in_ptr->fail_callback)); + } + /* del_attempt_count */ + *((int *)&msg_buf[msg_size]) = htonl(in_ptr->del_attempt_count); + msg_size+= sizeof(int); + + *out_size = msg_size; + return NS_TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_construct_sms_in -- + * + * Convert a network encoded sms message payload back to host encoded. + * Note that this is a bodge, for use whilst testing. This function + * is platform dependent, and assumes that all remote servers are running + * the same architecture. (PH 17/05/2001) + * + * Results: + * NS_TRUE - Conversion okay. + * NS_FALSE - Conversion failed + * + *---------------------------------------------------------------------- + */ +int +smsq_construct_sms_in(void *in_buf, int in_size, + void *out_buf, int *out_size) { + int msg_pos; + unsigned char *msg_buf; + struct smsq_sms_submit *out_ptr; + int str_len; + + msg_pos = 0; + msg_buf = (unsigned char *)in_buf; + out_ptr = (struct smsq_sms_submit *)out_buf; + + /* type_of_number */ + out_ptr->type_of_number = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* numbering_plan_id */ + out_ptr->numbering_plan_id = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* dest_address */ + str_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(out_ptr->dest_address, &msg_buf[msg_pos], str_len); + out_ptr->dest_address[str_len] = '\0'; + msg_pos += SMSQ_ALIGNED_SIZE(str_len); + /* protocol_id */ + out_ptr->protocol_id = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* data_coding_scheme */ + out_ptr->data_coding_scheme = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* validity_period / tp_mask */ + out_ptr->validity_period = msg_buf[msg_pos]; + out_ptr->tp_mask = msg_buf[msg_pos + 1]; + msg_pos+= sizeof(int); + /* safe_stored_p */ + out_ptr->safe_stored_p = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* user_data_len */ + out_ptr->user_data_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* user_data */ + memcpy(out_ptr->user_data, &msg_buf[msg_pos], out_ptr->user_data_len); + msg_pos += SMSQ_ALIGNED_SIZE(out_ptr->user_data_len); + /* unpacked_data_len */ + out_ptr->unpacked_data_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + + /* timestamp.tm_sec */ + out_ptr->timestamp.tm_sec = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* timestamp.tm_min */ + out_ptr->timestamp.tm_min = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* timestamp.tm_hour */ + out_ptr->timestamp.tm_hour = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* timestamp.tm_mday */ + out_ptr->timestamp.tm_mday = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* timestamp.tm_mon */ + out_ptr->timestamp.tm_mon = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* timestamp.tm_year */ + out_ptr->timestamp.tm_year = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + + /* smsc_timestamp.tm_sec */ + out_ptr->smsc_timestamp.tm_sec = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* smsc_timestamp.tm_min */ + out_ptr->smsc_timestamp.tm_min = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* smsc_timestamp.tm_hour */ + out_ptr->smsc_timestamp.tm_hour = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* smsc_timestamp.tm_mday */ + out_ptr->smsc_timestamp.tm_mday = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* smsc_timestamp.tm_mon */ + out_ptr->smsc_timestamp.tm_mon = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* smsc_timestamp.tm_year */ + out_ptr->smsc_timestamp.tm_year = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + /* smsc_timestamp.tm_tz */ + out_ptr->smsc_timestamp.tm_tz = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + + /* success_callback_p */ + out_ptr->success_callback_p = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + if (out_ptr->success_callback_p) { + /* success_callback */ + str_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(out_ptr->success_callback, &msg_buf[msg_pos], str_len); + out_ptr->success_callback[str_len] = '\0'; + msg_pos += SMSQ_ALIGNED_SIZE(str_len); + } + /* fail_callback_p */ + out_ptr->fail_callback_p = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + if (out_ptr->fail_callback_p) { + /* fail_callback */ + str_len = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + memcpy(out_ptr->fail_callback, &msg_buf[msg_pos], str_len); + out_ptr->fail_callback[str_len] = '\0'; + msg_pos += SMSQ_ALIGNED_SIZE(str_len); + } + /* del_attempt_count */ + out_ptr->del_attempt_count = ntohl(*((int *)&msg_buf[msg_pos])); + msg_pos += sizeof(int); + + *out_size = sizeof(struct smsq_sms_submit); + return NS_TRUE; +} + + + +/* + *---------------------------------------------------------------------- + * + * smsq_group_empty_p -- + * + * Checks if a group's message queue is empty. + * + * Results: + * + *---------------------------------------------------------------------- + */ +int +smsq_group_empty_p(struct q_group *group_ptr) { + int ret; + q_get_rlock(group_ptr); + if (group_ptr->queue_by_stat_ll[Q_MSG_STATUS_READY] == NULL) { + ret = NS_TRUE; + } else { + ret = NS_FALSE; + } + q_release_lock(group_ptr); + + return ret; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_set_group_process_flag -- + * + * Adjusts the counter indicating to the underlying qcluster layer whether + * this server will process messages for this group. The qcluster layer + * may reassign any pending/future messages for the specified group to + * another server. + * + * Results: + * + * Side effects: + * + *---------------------------------------------------------------------- + */ +int +smsq_set_group_process_p(struct q_group *group_ptr, int can_process_p) { + if (can_process_p == 1) { + q_set_can_process_p(group_ptr, 1); + } else { + q_set_can_process_p(group_ptr, -1); + } + return NS_TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_submit_sms -- + * + * Submits an sms to the specified group name. + * + * Results: + * + *---------------------------------------------------------------------- + */ +int +smsq_submit_sms(struct q_group *group_ptr, + char *msg_id_ptr, + int type_of_number, + int numbering_plan_id, + char *dest_address, + char *src_address, + int protocol_id, + int data_coding_scheme, + unsigned char validity_period, + unsigned char tp_mask, + int message_ref, + int safe_stored_p, + unsigned char *user_data, + int user_data_len, + int unpacked_data_len, + struct smsc_tm *smsc_timestamp_ptr, + char *fail_callback, + char *success_callback) { + struct timeval tv; + struct timezone tz; + time_t timep; + struct tm *datetime; + struct smsq_sms_submit payload; + + timep = time(NULL); + datetime = localtime(&timep); + gettimeofday(&tv, &tz); + + payload.type_of_number = type_of_number; + payload.numbering_plan_id = numbering_plan_id; + strcpy(payload.dest_address, dest_address); + if (src_address != NULL) { + strcpy(payload.src_address, src_address); + } else { + strcpy(payload.src_address, ""); + } + payload.protocol_id = protocol_id; + payload.data_coding_scheme = data_coding_scheme; + payload.validity_period = validity_period; + payload.tp_mask = tp_mask; + payload.message_ref = message_ref; + payload.safe_stored_p = safe_stored_p; + payload.user_data_len = user_data_len; + memcpy(payload.user_data, user_data, user_data_len); + payload.unpacked_data_len = unpacked_data_len; + memcpy(&payload.timestamp, datetime, sizeof(struct tm)); + if (smsc_timestamp_ptr != NULL) { + memcpy(&payload.smsc_timestamp, smsc_timestamp_ptr, sizeof(struct smsc_tm)); + } else { + memset(&payload.smsc_timestamp, 0, sizeof(struct smsc_tm)); + } + payload.fail_callback_p = 0; + payload.success_callback_p = 0; + if (fail_callback != NULL) { + payload.fail_callback_p = 1; + strcpy(payload.fail_callback, fail_callback); + } + if (success_callback != NULL) { + payload.success_callback_p = 1; + strcpy(payload.success_callback, success_callback); + } + payload.del_attempt_count = 0; + + sprintf(msg_id_ptr, "%s.%02d.%02d.%02d.%06d.%06d", g_server->svr_name, + datetime->tm_year, datetime->tm_mon, datetime->tm_mday, + (int) tv.tv_sec, (int) tv.tv_usec); + q_queue_msg(g_sms_submit_type, group_ptr, + msg_id_ptr, Q_MSG_STATUS_READY, + &payload, sizeof(struct smsq_sms_submit), + g_cluster_size); + return NS_TRUE; +} + + +/* + *---------------------------------------------------------------------- + * + * smsq_submit_ascii -- + * + * Submits an ascii sms onto the queue by using the smsq_submit_sms + * function api. + * + * Results: + * + *---------------------------------------------------------------------- + */ +int +smsq_submit_ascii(struct q_group *group_ptr, + char *msg_id_ptr, + char *msisdn_ptr, + char *src_msisdn_ptr, + char *message_ptr, + struct smsc_tm *smsc_timestamp_ptr, + char *fail_callback, + char *success_callback) { + unsigned char validity_period; + unsigned char bits7_msg[145]; // Should be 140, but just to be sure. + int bits7_len; + + /* + * Forcibly prevent messages greater than 160 characters. + */ + if (strlen(message_ptr) > 160) { + message_ptr[160] = '\0'; + } + + gsm0338_pack_7bit(bits7_msg, &bits7_len, message_ptr); + validity_period = (unsigned char) 0xaa; + + smsq_submit_sms(group_ptr, msg_id_ptr, + 1, /* type of number */ + 1, /* numbering plan_id */ + msisdn_ptr, /* dest_address */ + src_msisdn_ptr, /* src_address */ + 0, /* protocol_id */ + DCS_DEFAULT, /* data_coding_scheme */ + 0xaa, /* validity_period */ + TP_TYPE_SUBMIT | TP_STATUS_REP | TP_VAL_PER_REL, + /* tp_mask */ + 0, /* message_ref */ + 0, /* safe_stored_p */ + bits7_msg, + bits7_len, + strlen(message_ptr), /* unpacked_data_len */ + smsc_timestamp_ptr, + fail_callback, + success_callback); + return NS_TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_submit_8bit -- + * + * Submits an ascii sms onto the queue by using the smsq_submit_sms + * function api. + * + * Results: + * + *---------------------------------------------------------------------- + */ +int +smsq_submit_8bit(struct q_group *group_ptr, + char *msg_id_ptr, + char *msisdn_ptr, + char *src_msisdn_ptr, + unsigned char *message_ptr, + int message_size, + int unpacked_size, + int tp_flags, + int data_coding_scheme, + struct smsc_tm *smsc_timestamp_ptr, + char *fail_callback, + char *success_callback) { + smsq_submit_sms(group_ptr, msg_id_ptr, + 1, /* type of number */ + 1, /* numbering plan_id */ + msisdn_ptr, /* dest_address */ + src_msisdn_ptr, /* src_address */ + 0, /* protocol_id */ + data_coding_scheme, /* data_coding_scheme */ + 0xaa, /* validity_period */ + TP_TYPE_SUBMIT | TP_STATUS_REP | TP_VAL_PER_REL | tp_flags, + /* tp_mask */ + 0, /* message_ref */ + 0, /* safe_stored_p */ + message_ptr, + message_size, + unpacked_size, /* unpacked_data_len */ + smsc_timestamp_ptr, + fail_callback, + success_callback); + return NS_TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_set_msg_status -- + * + * Results: + * + *---------------------------------------------------------------------- + */ +int +smsq_set_msg_status(char *msg_id_ptr, + struct q_group *group_ptr, + int status) { + struct smsq_sms_submit *payload_ptr; + struct q_message *msg_ptr; + + if (status == Q_MSG_STATUS_FAILED || + status == Q_MSG_STATUS_COMPLETE) { + q_get_rlock(group_ptr); + msg_ptr = q_data_get_msg_entry(group_ptr, msg_id_ptr); + if (msg_ptr != NULL) { + payload_ptr = msg_ptr->msg_ptr; + if (status == Q_MSG_STATUS_FAILED && + payload_ptr->fail_callback_p) { + smsq_execute_tcl_response(msg_ptr, 0); + } else if (status == Q_MSG_STATUS_COMPLETE && + payload_ptr->success_callback_p) { + smsq_execute_tcl_response(msg_ptr, 1); + } + } + q_release_lock(group_ptr); + } + + q_msg_status_update(msg_id_ptr, group_ptr, status); + return NS_TRUE; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_get_msg_and_set_status -- + * + * Wrapper around the qcluster function that: + * Retrieves a message with the given status from the given group. The + * status of the message is then updated to the new status in one, single + * atomic operation. + * + * Results: + * Message pointer if a message was found. NULL otherwise. + * + *---------------------------------------------------------------------- + */ +struct q_message * +smsq_get_msg_and_set_status(struct q_group *group_ptr, + int cur_status, + int new_status) { + return q_get_msg_and_set_status(group_ptr, cur_status, new_status); +} + +/* + *---------------------------------------------------------------------- + * + * smsq_add_commands -- + * + * Adds the smsq api functions to the Tcl interpreter. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +smsq_add_commands(Tcl_Interp *interp, ClientData data) { + Tcl_CreateCommand(interp, "smsq_submit_ascii", smsq_tcl_submit_ascii, + NULL, NULL); + Tcl_CreateCommand(interp, "smsq_wait_for_group_activity", + smsq_tcl_wait_for_group_activity, + NULL, NULL); + Tcl_CreateCommand(interp, "smsq_get_next_group_msg", + smsq_tcl_get_next_group_msg, + NULL, NULL); + Tcl_CreateCommand(interp, "smsq_get_group_names", + smsq_tcl_get_group_names, + NULL, NULL); + Tcl_CreateCommand(interp, "smsq_get_server_name", + smsq_tcl_get_server_name, + NULL, NULL); + Tcl_CreateCommand(interp, "smsq_set_group_process_flag", + smsq_tcl_set_group_process_flag, + NULL, NULL); + Tcl_CreateCommand(interp, "smsq_set_msg_status", + smsq_tcl_set_msg_status, + NULL, NULL); + Tcl_CreateCommand(interp, "smsq_retrieve_ascii", + smsq_tcl_retrieve_ascii, + NULL, NULL); + Tcl_CreateCommand(interp, "smsq_get_queue_info", + smsq_tcl_get_queue_info, + NULL, NULL); + return NS_OK; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_tcl_submit_ascii -- + * + * Tcl API function to allow ascii sms to be added to the message queue + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +smsq_tcl_submit_ascii(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + struct q_group *group_ptr; + char msg_id[128]; + char *fail_callback_ptr; + char *success_callback_ptr; + char *src_msisdn = ""; + int arg_offset = 0; + + fail_callback_ptr = NULL; + success_callback_ptr = NULL; + + if (argc > 2 && !strcmp(argv[1],"-src")) { + src_msisdn = argv[2]; + arg_offset = 2; + } + if ((argc - arg_offset) < 4 || (argc - arg_offset) > 6) { + Tcl_AppendResult(interp, "Usage: ", argv[0], + " [-src src_msisdn] group_name msisdn message [failed_callback] [success_callback", NULL); + return TCL_ERROR; + } + + group_ptr = q_get_group_ptr(g_server, argv[arg_offset + 1]); + if (group_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown group name: ", argv[arg_offset + 1], NULL); + return TCL_ERROR; + } + + /* + * Set the fail_callback parameter if passed in + */ + if ((argc - arg_offset) > 4) { + fail_callback_ptr = argv[arg_offset + 4]; + } + + /* + * Set the success_callback parameter if passed in + */ + if ((argc - arg_offset) > 5) { + success_callback_ptr = argv[arg_offset + 5]; + } + + smsq_submit_ascii(group_ptr, + msg_id, + argv[arg_offset + 2], + src_msisdn, + argv[arg_offset + 3], + NULL, + fail_callback_ptr, + success_callback_ptr); + Tcl_AppendResult(interp, msg_id, NULL); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_tcl_retrieve_ascii -- + * + * Tcl API function to retrieve the contents of an ascii message + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +smsq_tcl_retrieve_ascii(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + struct q_group *group_ptr; + struct q_message *msg_ptr; + struct smsq_sms_submit *payload_ptr; + Tcl_Obj *list_ptr, *elem_ptr; + char bits8_msg[165]; + + + if (argc != 3) { + Tcl_AppendResult(interp, "Usage: ", argv[0], + " group_name msg_id", NULL); + return TCL_ERROR; + } + + list_ptr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + group_ptr = q_get_group_ptr(g_server, argv[1]); + if (group_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); + return TCL_ERROR; + } + + + q_get_rlock(group_ptr); + msg_ptr = q_data_get_msg_entry(group_ptr, argv[2]); + if (msg_ptr != NULL) { + payload_ptr = msg_ptr->msg_ptr; + + elem_ptr = Tcl_NewStringObj("msisdn", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewStringObj(payload_ptr->dest_address, -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("src_msisdn", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewStringObj(payload_ptr->src_address, -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("originating_msisdn", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewStringObj(payload_ptr->dest_address, -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("terminating_msisdn", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewStringObj(payload_ptr->src_address, -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("message", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + gsm0338_unpack_7bit(bits8_msg, payload_ptr->user_data, + payload_ptr->user_data_len, + payload_ptr->unpacked_data_len); + elem_ptr = Tcl_NewStringObj(bits8_msg, -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("smsc_year", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_year); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("smsc_month", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_mon); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("smsc_day", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_mday); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("smsc_hour", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_hour); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("smsc_min", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_min); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("smsc_sec", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_sec); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("smsc_tz", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewIntObj(payload_ptr->smsc_timestamp.tm_tz); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + + elem_ptr = Tcl_NewStringObj("dcs", -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + elem_ptr = Tcl_NewIntObj(payload_ptr->data_coding_scheme); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + } + q_release_lock(group_ptr); + + Tcl_SetObjResult(interp, list_ptr); + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * smsq_tcl_wait_for_group_activity -- + * + * Tcl API function to wait for activity within the specified group + * for the specified timeout period. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +smsq_tcl_wait_for_group_activity(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + struct q_group *group_ptr; + char msg_id[128]; + int timeout; + Ns_Time waittime; + int result; + + if (argc < 2 || argc > 3) { + Tcl_AppendResult(interp, "Usage: ", argv[0], + " group_name ?timeout?", NULL); + return TCL_ERROR; + } else { + if (argc == 3) { + if (Tcl_GetInt(interp, argv[2], &timeout) != TCL_OK) { + return TCL_ERROR; + } + } + } + + group_ptr = q_get_group_ptr(g_server, argv[1]); + if (group_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); + return TCL_ERROR; + } + /* + * Call the underlying Ns_Cond function for this thread event. + */ + if (argc == 2) { + Ns_CondWait(&group_ptr->event, &group_ptr->event_lock); + interp->result = "1"; + Ns_MutexUnlock(&group_ptr->event_lock); + } else { + Ns_GetTime(&waittime); + Ns_IncrTime(&waittime, timeout, 0); + result = Ns_CondTimedWait(&group_ptr->event, &group_ptr->event_lock, + &waittime); + Ns_MutexUnlock(&group_ptr->event_lock); + switch (result) { + case NS_OK: + interp->result = "1"; + break; + case NS_TIMEOUT: + interp->result = "0"; + break; + default: + return TCL_ERROR; + break; + } + } + + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * smsq_tcl_get_next_group_msg -- + * + * Tcl API function to retrieve a message within the given group, + * returning the message id. Returns an empty string if no message + * found. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +smsq_tcl_get_next_group_msg(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + struct q_message *msg_ptr; + struct q_group *group_ptr; + + if (argc != 2) { + Tcl_AppendResult(interp, "Usage: ", argv[0], + " group_name", NULL); + return TCL_ERROR; + } + + group_ptr = q_get_group_ptr(g_server, argv[1]); + if (group_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); + return TCL_ERROR; + } + + /* + * Get the next available message + */ + msg_ptr = smsq_get_msg_and_set_status(group_ptr, + Q_MSG_STATUS_READY, + Q_MSG_STATUS_PROCESSING); + if (msg_ptr == NULL) { + Tcl_AppendResult(interp, NULL); + } else { + Tcl_AppendResult(interp, msg_ptr->msg_id, NULL); + } +// Ns_Log(Notice, "Exiting smsq_tcl_get_next_group_msg %x", msg_ptr); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_tcl_set_msg_status -- + * + * Set the status of the given message id. If status is "complete", the + * message is removed from the given group queue. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +smsq_tcl_set_msg_status(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + struct q_group *group_ptr; + int status; + + if (argc != 4) { + Tcl_AppendResult(interp, "Usage: ", argv[0], + " group_name msg_id status", NULL); + return TCL_ERROR; + } + + group_ptr = q_get_group_ptr(g_server, argv[1]); + if (group_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); + return TCL_ERROR; + } + + /* + * Work out the actual C status value + */ + if (STREQ(argv[3], "ready")) { + status = Q_MSG_STATUS_READY; + } else if (STREQ(argv[3], "delayed")) { + status = Q_MSG_STATUS_DELAYED; + } else if (STREQ(argv[3], "processing")) { + status = Q_MSG_STATUS_PROCESSING; + } else if (STREQ(argv[3], "complete")) { + status = Q_MSG_STATUS_COMPLETE; + } else if (STREQ(argv[3], "failed")) { + status = Q_MSG_STATUS_FAILED; + } else { + Tcl_AppendResult(interp, "Unknown status: should be ", + "ready, ", + "delayed, ", + "processing, ", + "complete or", + "failed", NULL); + return TCL_ERROR; + } + + /* + * Set the status value. + */ +// q_msg_status_update(argv[2], group_ptr, status); + smsq_set_msg_status(argv[2], group_ptr, status); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_tcl_set_group_process_flag -- + * + * Set the group process flag for the specified group. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +smsq_tcl_set_group_process_flag(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + struct q_group *group_ptr; + int process_p; + + if (argc != 3) { + Tcl_AppendResult(interp, "Usage: ", argv[0], + " group_name process_flag", NULL); + return TCL_ERROR; + } + + group_ptr = q_get_group_ptr(g_server, argv[1]); + if (group_ptr == NULL) { + Tcl_AppendResult(interp, "Unknown group name: ", argv[1], NULL); + return TCL_ERROR; + } + if (Tcl_GetInt(interp, argv[2], &process_p) != TCL_OK) { + return TCL_ERROR; + } + if (process_p < 0 || process_p > 1) { + Tcl_AppendResult(interp, "Invalid process_p flag : ", argv[2], NULL); + return TCL_ERROR; + } + + /* + * Call the C function to set the process flag. + */ + smsq_set_group_process_p(group_ptr, + process_p); + + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * smsq_tcl_get_group_names -- + * + * Return a list of group names + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +smsq_tcl_get_group_names(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + char *group_name_ptr; + int process_p; + Tcl_HashEntry *search_ptr; + Tcl_HashSearch search; + Tcl_Obj *list_ptr, *elem_ptr; + + if (argc != 1) { + Tcl_AppendResult(interp, "Usage: ", argv[0], NULL); + return TCL_ERROR; + } + + list_ptr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + + q_get_rlock(g_server); + search_ptr = Tcl_FirstHashEntry(&g_server->groups_ht, &search); + while (search_ptr != NULL) { + group_name_ptr = Tcl_GetHashKey(&g_server->groups_ht, search_ptr); + elem_ptr = Tcl_NewStringObj(group_name_ptr, -1); + Tcl_ListObjAppendElement(interp, list_ptr, elem_ptr); + search_ptr = Tcl_NextHashEntry(&search); + } + q_release_lock(g_server); + + Tcl_SetObjResult(interp, list_ptr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_tcl_get_queue_info -- + * + * Return a complete snapshop of all queues. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +smsq_tcl_get_queue_info(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + char *group_name_ptr; + int process_p; + Tcl_HashEntry *search_ptr; + Tcl_HashSearch search; + struct q_message_search msg_search; + Tcl_Obj *server_list_ptr, *group_list_ptr, *msg_list_ptr, *elem_ptr; + struct q_server *server_ptr; + struct q_group *group_ptr; + struct q_message *message_ptr; + int i; + + if (argc != 1) { + Tcl_AppendResult(interp, "Usage: ", argv[0], NULL); + return TCL_ERROR; + } + + server_list_ptr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + + q_get_rlock(g_server); + for (i = 0; i < g_cluster_size; i++) { + server_ptr = g_cluster_order[i]; + elem_ptr = Tcl_NewStringObj(server_ptr->svr_name, -1); + Tcl_ListObjAppendElement(interp, server_list_ptr, elem_ptr); + + group_list_ptr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + + search_ptr = Tcl_FirstHashEntry(&server_ptr->groups_ht, &search); + while (search_ptr != NULL) { + /* add group name */ + group_name_ptr = Tcl_GetHashKey(&server_ptr->groups_ht, search_ptr); + group_ptr = Tcl_GetHashValue(search_ptr); + elem_ptr = Tcl_NewStringObj(group_name_ptr, -1); + Tcl_ListObjAppendElement(interp, group_list_ptr, elem_ptr); + + msg_list_ptr = Tcl_NewListObj(0, (Tcl_Obj **) NULL); + message_ptr = q_data_first_msg_entry(group_ptr, + -1, + &msg_search); + while (message_ptr != NULL) { + /* + * Add this individual message. + */ + elem_ptr = Tcl_NewStringObj(message_ptr->msg_id, -1); + Tcl_ListObjAppendElement(interp, msg_list_ptr, elem_ptr); + message_ptr = q_data_next_msg_entry(&msg_search); + } + Tcl_ListObjAppendElement(interp, group_list_ptr, msg_list_ptr); + + search_ptr = Tcl_NextHashEntry(&search); + } + q_release_lock(server_ptr); + Tcl_ListObjAppendElement(interp, server_list_ptr, group_list_ptr); + } + + Tcl_SetObjResult(interp, server_list_ptr); + return TCL_OK; +} + + +/* + *---------------------------------------------------------------------- + * + * smsq_tcl_get_server_name -- + * + * Return a list of group names + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +smsq_tcl_get_server_name(ClientData data, Tcl_Interp *interp, + int argc, char *argv[]) { + q_get_rlock(g_server); + Tcl_AppendResult(interp, g_server->svr_name, NULL); + q_release_lock(g_server); + + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * smsq_tcl_execute_tcl -- + * + * Executes piece of tcl. + * + * Results: + * + *---------------------------------------------------------------------- + */ +static int +smsq_execute_tcl_response(struct q_message *message_ptr, + int success_p) { + struct smsq_sms_submit *payload_ptr; + Ns_DString script; + Ns_DString result; + int status; + unsigned char bits8_msg[165]; + + Ns_DStringInit(&script); + Ns_DStringInit(&result); + payload_ptr = message_ptr->msg_ptr; + + if (success_p) { + if (!payload_ptr->success_callback_p) { + return NS_FALSE; + } else { + Ns_DStringVarAppend(&script, payload_ptr->success_callback, " 1 ", NULL); + } + } else { + if (!payload_ptr->fail_callback_p) { + return NS_FALSE; + } else { + Ns_DStringVarAppend(&script, payload_ptr->fail_callback, " 0 ", NULL); + } + } + + Ns_DStringVarAppend(&script, message_ptr->msg_id, " ", NULL); + + Ns_DStringVarAppend(&script, "[list ", NULL); + + Ns_DStringVarAppend(&script, "msisdn", " {", NULL); + Ns_DStringVarAppend(&script, payload_ptr->dest_address,"} ", NULL); + + Ns_DStringVarAppend(&script, "src_msisdn", " {", NULL); + Ns_DStringVarAppend(&script, payload_ptr->src_address,"} ", NULL); + + Ns_DStringVarAppend(&script, "message", " {", NULL); + gsm0338_unpack_7bit(bits8_msg, payload_ptr->user_data, + payload_ptr->user_data_len, + payload_ptr->unpacked_data_len); + Ns_DStringVarAppend(&script, bits8_msg, "}", NULL); + + Ns_DStringVarAppend(&script, "]", NULL); + + status = Ns_TclEval(&result, g_aolserver_name, + script.string); + return NS_TRUE; +} Index: openacs-4/contrib/misc/smsc/smsq/smsq.h =================================================================== RCS file: /usr/local/cvsroot/openacs-4/contrib/misc/smsc/smsq/smsq.h,v diff -u -N --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ openacs-4/contrib/misc/smsc/smsq/smsq.h 2 Dec 2003 06:17:38 -0000 1.1 @@ -0,0 +1,165 @@ +/* + * This file is part of Smsq. + * + * Smsq is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Smsq is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Smsq; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Peter Harper + * + * $Id: smsq.h,v 1.1 2003/12/02 06:17:38 rmello Exp $ + */ + +#ifndef _SMSQ_H +#define _SMSQ_H + +#include +#include "gsm0338.h" + +#define Version "1.3" + +#define DEBUG 1 + +/* + * Queue type definitions + */ +#define SMSQ_SUBMIT_TYPE_ID 0 +#define SMSQ_SUBMIT_TYPE_NAME "sms_sub" + +/* + * TP masks + */ +#define TP_REPLY_PATH 128 +#define TP_UDHI 64 +#define TP_STATUS_REP 32 +#define TP_VAL_PER_NONE 0 +#define TP_VAL_PER_REL 16 +#define TP_VAL_PER_ABS 16 | 8 +#define TP_REJ_DUP 4 +#define TP_TYPE_SUBMIT 1 + +#define DCS_CLASS0 16 | 0 +#define DCS_CLASS1 16 | 1 +#define DCS_CLASS2 16 | 2 +#define DCS_CLASS3 16 | 3 +#define DCS_DEFAULT 0 +#define DCS_8BIT 4 +#define DCS_UCS 8 +#define DCS_RESERVED 12 + +/* + * Queue data structures. + * NB: Any changes in these data structures *MUST* be reflected in the + * smsq_construct_sms_out and smsq_construct_sms_in functions. + */ + +struct smsc_tm { + int tm_sec; /* seconds */ + int tm_min; /* minutes */ + int tm_hour; /* hours */ + int tm_mday; /* day of the month */ + int tm_mon; /* month */ + int tm_year; /* full year */ + int tm_tz; /* timezome, relation to GMT */ +}; + +struct smsq_sms_submit { + int type_of_number; + int numbering_plan_id; + char dest_address[14]; + char src_address[14]; + int protocol_id; + int data_coding_scheme; + unsigned char validity_period; + unsigned char tp_mask; + + int message_ref; + int safe_stored_p; + unsigned char user_data[165]; // Should be 160, but just being safe. + int user_data_len; + int unpacked_data_len; + struct tm timestamp; + struct smsc_tm smsc_timestamp; + + int success_callback_p; + int fail_callback_p; + char success_callback[128]; + char fail_callback[128]; + + int del_attempt_count; +}; + +/* + * Function prototypes. + */ +extern int +smsq_set_group_process_p(struct q_group *group_ptr, int can_process_p); + +int +smsq_submit_sms(struct q_group *group_ptr, + char *msg_id_ptr, + int type_of_number, + int numbering_plan_id, + char *dest_address, + char *src_address, + int protocol_id, + int data_coding_scheme, + unsigned char validity_period, + unsigned char tp_mask, + int message_ref, + int safe_stored_p, + unsigned char *user_data, + int user_data_len, + int unpacked_data_len, + struct smsc_tm *smsc_timestamp, + char *fail_callback, + char *success_callback); + +extern int +smsq_submit_8bit(struct q_group *group_ptr, + char *msg_id_ptr, + char *msisdn_ptr, + char *src_msisdn_ptr, + unsigned char *message_ptr, + int message_size, + int unpacked_size, + int tp_flags, + int data_coding_scheme, + struct smsc_tm *smsc_timestamp, + char *fail_callback, + char *success_callback); + +extern int +smsq_submit_ascii(struct q_group *group_ptr, + char *msg_id_ptr, + char *msisdn_ptr, + char *src_msisdn_ptr, + char *message_ptr, + struct smsc_tm *smsc_timestamp, + char *fail_callback, + char *success_callback); + +struct q_message * +smsq_get_msg_and_set_status(struct q_group *group_ptr, + int cur_status, + int new_status); + +int +smsq_set_msg_status(char *msg_id_ptr, + struct q_group *group_ptr, + int status); +int +smsq_group_empty_p(struct q_group *group_ptr); + + +#endif