* This module handles shared rooms, inter-Citadel mail, and outbound
* mailing list processing.
*
- * Copyright (c) 2000-2011 by the citadel.org team
+ * Copyright (c) 2000-2012 by the citadel.org team
*
* This program is open source software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include "netmail.h"
#include "ctdl_module.h"
+/* comes from lookup3.c from libcitadel... */
+extern uint32_t hashlittle( const void *key, size_t length, uint32_t initval);
-
+typedef struct __roomlists {
+ RoomProcList *rplist;
+ HashList *RoomsInterestedIn;
+}roomlists;
/*
* When we do network processing, it's accomplished in two passes; one to
* gather a list of rooms and one to actually do them. It's ok that rplist
*/
struct RoomProcList *rplist = NULL;
+int GetNetworkedRoomNumbers(const char *DirName, HashList *DirList)
+{
+ DIR *filedir = NULL;
+ struct dirent *d;
+ struct dirent *filedir_entry;
+ long RoomNR;
+ long Count = 0;
+
+ filedir = opendir (DirName);
+ if (filedir == NULL) {
+ return 0;
+ }
+ d = (struct dirent *)malloc(offsetof(struct dirent, d_name) + PATH_MAX + 1);
+ if (d == NULL) {
+ return 0;
+ }
+
+ while ((readdir_r(filedir, d, &filedir_entry) == 0) &&
+ (filedir_entry != NULL))
+ {
+ RoomNR = atol(filedir_entry->d_name);
+ if (RoomNR != 0) {
+ Count++;
+ Put(DirList, LKEY(RoomNR), &Count, reference_free_handler);
+ }
+ }
+ free(d);
+ closedir(filedir);
+ return Count;
+}
+/*
+ * Batch up and send all outbound traffic from the current room
+ */
+void network_queue_interesting_rooms(struct ctdlroom *qrbuf, void *data) {
+ int i;
+ struct RoomProcList *ptr;
+ long QRNum = qrbuf->QRnumber;
+ void *v;
+ roomlists *RP = (roomlists*) data;
+
+ if (!GetHash(RP->RoomsInterestedIn, LKEY(QRNum), &v))
+ return;
+
+ ptr = (struct RoomProcList *) malloc(sizeof (struct RoomProcList));
+ if (ptr == NULL) return;
+
+ ptr->namelen = strlen(qrbuf->QRname);
+ if (ptr->namelen > ROOMNAMELEN)
+ ptr->namelen = ROOMNAMELEN - 1;
+
+ memcpy (ptr->name, qrbuf->QRname, ptr->namelen);
+ ptr->name[ptr->namelen] = '\0';
+ ptr->QRNum = qrbuf->QRnumber;
+
+ for (i = 0; i < ptr->namelen; i++)
+ {
+ ptr->lcname[i] = tolower(ptr->name[i]);
+ }
+
+ ptr->lcname[ptr->namelen] = '\0';
+ ptr->key = hashlittle(ptr->lcname, ptr->namelen, 9872345);
+ ptr->next = RP->rplist;
+ RP->rplist = ptr;
+}
+
/*
* Batch up and send all outbound traffic from the current room
*/
void network_queue_room(struct ctdlroom *qrbuf, void *data) {
+ int i;
struct RoomProcList *ptr;
+ if (qrbuf->QRdefaultview == VIEW_QUEUE)
+ return;
ptr = (struct RoomProcList *) malloc(sizeof (struct RoomProcList));
if (ptr == NULL) return;
- safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name);
+ ptr->namelen = strlen(qrbuf->QRname);
+ if (ptr->namelen > ROOMNAMELEN)
+ ptr->namelen = ROOMNAMELEN - 1;
+
+ memcpy (ptr->name, qrbuf->QRname, ptr->namelen);
+ ptr->name[ptr->namelen] = '\0';
+ ptr->QRNum = qrbuf->QRnumber;
+
+ for (i = 0; i < ptr->namelen; i++)
+ {
+ ptr->lcname[i] = tolower(ptr->name[i]);
+ }
+ ptr->lcname[ptr->namelen] = '\0';
+ ptr->key = hashlittle(ptr->lcname, ptr->namelen, 9872345);
+
begin_critical_section(S_RPLIST);
ptr->next = rplist;
rplist = ptr;
end_critical_section(S_RPLIST);
}
-void destroy_network_queue_room(void)
+void destroy_network_queue_room(RoomProcList *rplist)
{
struct RoomProcList *cur, *p;
cur = rplist;
- begin_critical_section(S_RPLIST);
while (cur != NULL)
{
p = cur->next;
free (cur);
cur = p;
}
- rplist = NULL;
- end_critical_section(S_RPLIST);
}
-
+void destroy_network_queue_room_locked (void)
+{
+ begin_critical_section(S_RPLIST);
+ destroy_network_queue_room(rplist);
+ end_critical_section(S_RPLIST);
+}
void network_do_queue(void) {
static int doing_queue = 0;
static time_t last_run = 0L;
- struct RoomProcList *ptr;
int full_processing = 1;
- char *working_ignetcfg;
- NetMap *the_netmap = NULL;
+ HashList *working_ignetcfg;
+ HashList *the_netmap = NULL;
int netmap_changed = 0;
+ roomlists RL;
/*
* Run the full set of processing tasks no more frequently
}
doing_queue = 1;
+ begin_critical_section(S_RPLIST);
+ RL.rplist = rplist;
+ rplist = NULL;
+ end_critical_section(S_RPLIST);
+
+ RL.RoomsInterestedIn = NewHash(1, lFlathash);
+ if (full_processing &&
+ (GetNetworkedRoomNumbers(ctdl_netcfg_dir, RL.RoomsInterestedIn)==0))
+ {
+ doing_queue = 0;
+ DeleteHash(&RL.RoomsInterestedIn);
+ if (RL.rplist == NULL)
+ return;
+ }
/* Load the IGnet Configuration into memory */
- working_ignetcfg = load_working_ignetcfg();
+ working_ignetcfg = load_ignetcfg();
/*
* Load the network map and filter list into memory.
*/
- the_netmap = read_network_map();
- load_network_filter_list();
+ if (!server_shutting_down)
+ the_netmap = read_network_map();
+ if (!server_shutting_down)
+ load_network_filter_list();
/*
* Go ahead and run the queue
*/
if (full_processing && !server_shutting_down) {
syslog(LOG_DEBUG, "network: loading outbound queue\n");
- CtdlForEachRoom(network_queue_room, NULL);
+ CtdlForEachRoom(network_queue_interesting_rooms, &RL);
}
- if (rplist != NULL) {
+ if ((RL.rplist != NULL) && (!server_shutting_down)) {
+ RoomProcList *ptr, *cmp;
+ ptr = RL.rplist;
syslog(LOG_DEBUG, "network: running outbound queue\n");
- while (rplist != NULL && !server_shutting_down) {
- char spoolroomname[ROOMNAMELEN];
- safestrncpy(spoolroomname, rplist->name, sizeof spoolroomname);
- begin_critical_section(S_RPLIST);
-
- /* pop this record off the list */
- ptr = rplist;
- rplist = rplist->next;
- free(ptr);
-
- /* invalidate any duplicate entries to prevent double processing */
- for (ptr=rplist; ptr!=NULL; ptr=ptr->next) {
- if (!strcasecmp(ptr->name, spoolroomname)) {
- ptr->name[0] = 0;
+ while (ptr != NULL && !server_shutting_down) {
+
+ cmp = ptr->next;
+
+ while (cmp != NULL) {
+ if ((cmp->namelen > 0) &&
+ (cmp->key == ptr->key) &&
+ (cmp->namelen == ptr->namelen) &&
+ (strcmp(cmp->lcname, ptr->lcname) == 0))
+ {
+ cmp->namelen = 0;
}
+ cmp = cmp->next;
}
- end_critical_section(S_RPLIST);
- if (spoolroomname[0] != 0) {
- network_spoolout_room(spoolroomname,
+ if (ptr->namelen > 0) {
+ network_spoolout_room(ptr,
working_ignetcfg,
the_netmap);
}
+ ptr = ptr->next;
}
}
&netmap_changed);
}
- /* Save the network map back to disk */
- write_network_map(the_netmap, netmap_changed);
-
/* Free the filter list in memory */
free_netfilter_list();
+ /* Save the network map back to disk */
+ if (netmap_changed) {
+ StrBuf *MapStr = SerializeNetworkMap(the_netmap);
+ CtdlPutSysConfig(IGNETMAP, SmashStrBuf(&MapStr));
+ }
+
+ /* combine singe message files into one spool entry per remote node. */
network_consolidate_spoolout(working_ignetcfg, the_netmap);
- free(working_ignetcfg);
+
+ /* shut down. */
+
+ DeleteHash(&the_netmap);
+
+ DeleteHash(&working_ignetcfg);
syslog(LOG_DEBUG, "network: queue run completed\n");
if (full_processing) {
last_run = time(NULL);
}
-
+ DeleteHash(&RL.RoomsInterestedIn);
+ destroy_network_queue_room(RL.rplist);
doing_queue = 0;
}
return 0;
}
+int NTTDebugEnabled = 0;
+
+/*
+ * network_talking_to() -- concurrency checker
+ */
+static HashList *nttlist = NULL;
+int network_talking_to(const char *nodename, long len, int operation) {
+
+ int retval = 0;
+ HashPos *Pos = NULL;
+ void *vdata;
+
+ begin_critical_section(S_NTTLIST);
+
+ switch(operation) {
+
+ case NTT_ADD:
+ if (nttlist == NULL)
+ nttlist = NewHash(1, NULL);
+ Put(nttlist, nodename, len, NewStrBufPlain(nodename, len), HFreeStrBuf);
+ if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: added <%s>\n", nodename);
+ break;
+ case NTT_REMOVE:
+ if ((nttlist == NULL) ||
+ (GetCount(nttlist) == 0))
+ break;
+ Pos = GetNewHashPos(nttlist, 1);
+ if (GetHashPosFromKey (nttlist, nodename, len, Pos))
+ DeleteEntryFromHash(nttlist, Pos);
+ DeleteHashPos(&Pos);
+ if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: removed <%s>\n", nodename);
+
+ break;
+
+ case NTT_CHECK:
+ if ((nttlist == NULL) ||
+ (GetCount(nttlist) == 0))
+ break;
+ if (GetHash(nttlist, nodename, len, &vdata))
+ retval ++;
+ if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: have [%d] <%s>\n", retval, nodename);
+ break;
+ }
+
+ end_critical_section(S_NTTLIST);
+ return(retval);
+}
+
+void cleanup_nttlist(void)
+{
+ begin_critical_section(S_NTTLIST);
+ DeleteHash(&nttlist);
+ end_critical_section(S_NTTLIST);
+}
+
+
+
+void network_logout_hook(void)
+{
+ CitContext *CCC = MyContext();
+
+ /*
+ * If we were talking to a network node, we're not anymore...
+ */
+ if (!IsEmptyStr(CCC->net_node)) {
+ network_talking_to(CCC->net_node, strlen(CCC->net_node), NTT_REMOVE);
+ CCC->net_node[0] = '\0';
+ }
+}
+void network_cleanup_function(void)
+{
+ struct CitContext *CCC = CC;
+
+ if (!IsEmptyStr(CCC->net_node)) {
+ network_talking_to(CCC->net_node, strlen(CCC->net_node), NTT_REMOVE);
+ CCC->net_node[0] = '\0';
+ }
+}
+
/*
* Module entry point
*/
+void SetNTTDebugEnabled(const int n)
+{
+ NTTDebugEnabled = n;
+}
+
CTDL_MODULE_INIT(network)
{
if (!threading)
{
+ CtdlRegisterDebugFlagHook(HKEY("networktalkingto"), SetNTTDebugEnabled, &NTTDebugEnabled);
+ CtdlRegisterCleanupHook(cleanup_nttlist);
+ CtdlRegisterSessionHook(network_cleanup_function, EVT_STOP);
+ CtdlRegisterSessionHook(network_logout_hook, EVT_LOGOUT);
CtdlRegisterProtoHook(cmd_nsyn, "NSYN", "Synchronize room to node");
CtdlRegisterRoomHook(network_room_handler);
- CtdlRegisterCleanupHook(destroy_network_queue_room);
+ CtdlRegisterCleanupHook(destroy_network_queue_room_locked);
CtdlRegisterSessionHook(network_do_queue, EVT_TIMER);
}
return "network";