void rmcache(struct cached_page *cp) { struct phys_block *pb = cp->page; int hv_dev = makehash(cp->dev, cp->dev_offset); assert(cp->page->flags & PBF_INCACHE); cp->page->flags &= ~PBF_INCACHE; rmhash_bydev(cp, &cache_hash_bydev[hv_dev]); if(cp->ino != VMC_NO_INODE) { int hv_ino = makehash(cp->ino, cp->ino_offset); rmhash_byino(cp, &cache_hash_byino[hv_ino]); } assert(cp->page->refcount >= 1); cp->page->refcount--; lru_rm(cp); if(pb->refcount == 0) { assert(pb->phys != MAP_NONE); free_mem(ABS2CLICK(pb->phys), 1); SLABFREE(pb); } SLABFREE(cp); }
void mkauthhash(const char *s,unsigned int len,char *h) /* This is a string that should be the same for all messages from a given */ /* author. Doesn't have to be the real rfc822 address. We look for a '@' */ /* and grab everything up to the next '>', ' ', or ';'. We go back the same */ /* way, then take everything up to the '@' or the first '-'. The latter */ /* avoids problems with posters that band their addresses. */ { unsigned int i,j,k,l; char ch; j = k = l = 0; i = byte_rchr(s,len,'@'); if (i < len) { /* if not then i=sa->len, j=k=l=0 */ j = i; while (++j < len) { /* if not found, then j=sa->len */ ch = s[j]; if (ch == '>' || ch == ' ' || ch == ';') break; } k = i; while (k > 0) { /* k <= i */ ch = s[--k]; if (ch == '<' || ch == ' ' || ch == ';') break; } l = k; /* k <= l <= i; */ while (l < i && s[l] != '-') ++l; if (!stralloc_copyb(&dummy,s + k, l - k)) die_nomem(); if (!stralloc_catb(&dummy,s + i, j - i)) die_nomem(); makehash(dummy.s,dummy.len,h); } else /* use entire line if no '@' found */ makehash(s,len,h); }
int do_vacation(action_list_t *a, char *addr, char *fromaddr, char *subj, const char *msg, int days, int mime, const char *handle) { action_list_t *b = NULL; /* see if this conflicts with any previous actions taken on this message */ while (a != NULL) { b = a; if (a->a == ACTION_REJECT || a->a == ACTION_VACATION) /* vacation can't be used twice */ return SIEVE_RUN_ERROR; a = a->next; } /* add to the action list */ a = (action_list_t *) xmalloc(sizeof(action_list_t)); if (a == NULL) return SIEVE_NOMEM; a->a = ACTION_VACATION; a->cancel_keep = 0; a->u.vac.send.addr = addr; a->u.vac.send.fromaddr = fromaddr; a->u.vac.send.subj = subj; /* user specified subject */ a->u.vac.send.msg = msg; a->u.vac.send.mime = mime; if (handle) makehash(a->u.vac.autoresp.hash, addr, handle, NULL); else makehash(a->u.vac.autoresp.hash, addr, fromaddr, msg); a->u.vac.autoresp.days = days; a->next = NULL; b->next = a; return 0; }
void cache_sanitycheck_internal(void) { int h; int n = 0; int byino = 0; int withino = 0; int bydev_total = 0, lru_total = 0; struct cached_page *cp; for(h = 0; h < HASHSIZE; h++) { for(cp = cache_hash_bydev[h]; cp; cp = cp->hash_next_dev) { assert(cp->dev != NO_DEV); assert(h == makehash(cp->dev, cp->dev_offset)); assert(cp == find_cached_page_bydev(cp->dev, cp->dev_offset, cp->ino, cp->ino_offset)); if(cp->ino != VMC_NO_INODE) withino++; bydev_total++; n++; assert(n < 1500000); } for(cp = cache_hash_byino[h]; cp; cp = cp->hash_next_ino) { assert(cp->dev != NO_DEV); assert(cp->ino != VMC_NO_INODE); assert(h == makehash(cp->ino, cp->ino_offset)); byino++; n++; assert(n < 1500000); } } assert(byino == withino); if(lru_newest) { assert(lru_oldest); assert(!lru_newest->newer); assert(!lru_oldest->older); } else { assert(!lru_oldest); } for(cp = lru_oldest; cp; cp = cp->newer) { struct cached_page *newer = cp->newer, *older = cp->older; if(newer) assert(newer->older == cp); if(older) assert(older->newer == cp); lru_total++; } assert(lru_total == bydev_total); assert(lru_total == cached_pages); }
void hash_add(hash *hashtable[], bstring key, bstring value, unsigned int tablesize) { hash *tmp = NULL; unsigned int thehash = makehash(key, tablesize); /* if there's not a value at thehash ... */ if (hashtable[thehash]->key == NULL) { hashtable[thehash] = hash_alloc(); hashtable[thehash]->key = bstrcpy(key); hashtable[thehash]->value = bstrcpy(value); } else { /* traverse through the linked list (collision resolution) */ for (tmp = hashtable[thehash]; tmp != NULL; tmp = tmp->next) { if (tmp->next == NULL) { tmp->next = hash_alloc(); tmp->next->key = bstrcpy(key); tmp->next->value = bstrcpy(value); break; } } } }
//This function creates a routing table dynamically. //All the entries in the table are initialized to NULL pointers. //Then for all the neighbors with a direct link, create a routing entry using the neighbor itself as the //next hop node, and insert this routing entry into the routing table. //The dynamically created routing table structure is returned. routingtable_t* routingtable_create() { routingtable_t *routingTable = malloc(sizeof(routingtable_entry_t *)*MAX_ROUTINGTABLE_SLOTS); memset(routingTable, 0, sizeof(routingtable_entry_t *)*MAX_ROUTINGTABLE_SLOTS); //initialize all hash entries to NULL for (int i = 0; i < MAX_ROUTINGTABLE_SLOTS; i++) { routingTable->hash[i] = NULL; } //initialize neighbors int* nbrArray = topology_getNbrArray(); int numNbrs = topology_getNbrNum(); for (int j = 0; j < numNbrs; j++){ routingtable_entry_t *temp = malloc(sizeof(routingtable_entry_t)); temp->destNodeID = nbrArray[j]; temp->nextNodeID = nbrArray[j]; temp->next = NULL; int tempHash = makehash(nbrArray[j]); if (routingTable->hash[tempHash] == NULL) { routingTable->hash[tempHash] = temp; } else { routingtable_entry_t *tempEntry = routingTable->hash[tempHash]; while (tempEntry->next != NULL) { tempEntry = tempEntry->next; } tempEntry->next = temp; } } //routingtable_print(routingTable); free(nbrArray); return routingTable; }
//This function updates the routing table using the given destination node ID and next hop's node ID. //If the routing entry for the given destination already exists, update the existing routing entry. //If the routing entry of the given destination is not there, add one with the given next node ID. //Each slot in routing table contains a linked list of routing entries due to conflicting hash keys (differnt hash keys (destination node ID here) may have same hash values (slot entry number here)). //To add an routing entry to the hash table: //First use the hash function makehash() to get the slot number in which this routing entry should be stored. //Then append the routing entry to the linked list in that slot. void routingtable_setnextnode(routingtable_t* routingtable, int destNodeID, int nextNodeID) { int hashNum = makehash(destNodeID); if (routingtable->hash[hashNum]) { //if there's something at the hash routingtable_entry_t *currentEntry = routingtable->hash[hashNum]; if (currentEntry->destNodeID == destNodeID){ //if it's the entry we want.. currentEntry->nextNodeID = nextNodeID; //printf("Set entry with destID %d to nextID %d.\n", currentEntry->destNodeID, currentEntry->nextNodeID); return; } while (currentEntry->next != NULL) { //else look through linked list for entry we want currentEntry = currentEntry->next; if (currentEntry->destNodeID == destNodeID){ currentEntry->nextNodeID = nextNodeID; //printf("Set entry with destID %d to nextID %d.\n", currentEntry->destNodeID, currentEntry->nextNodeID); return; } } //if we couldn't find the entry we wanted at the hash, create it currentEntry->next = malloc(sizeof(routingtable_entry_t)); currentEntry->next->destNodeID = destNodeID; currentEntry->next->nextNodeID = nextNodeID; currentEntry->next->next = NULL; //printf("Created entry with destID %d and nextID %d.\n", currentEntry->next->destNodeID, currentEntry->next->nextNodeID); return; } else { //if nothing at the hash.. create it routingtable->hash[hashNum] = malloc(sizeof(routingtable_entry_t)); routingtable->hash[hashNum]->destNodeID = destNodeID; routingtable->hash[hashNum]->nextNodeID = nextNodeID; routingtable->hash[hashNum]->next = NULL; //printf("Created entry with destID %d and nextID %d.\n", routingtable->hash[hashNum]->destNodeID, routingtable->hash[hashNum]->nextNodeID); return; } }
static void addcache_byino(struct cached_page *hb) { int hv_ino = makehash(hb->ino, hb->ino_offset); assert(hb->ino != VMC_NO_INODE); hb->hash_next_ino = cache_hash_byino[hv_ino]; cache_hash_byino[hv_ino] = hb; }
//这个函数使用给定的目的节点ID和下一跳节点ID更新路由表. //如果给定目的节点的路由条目已经存在, 就更新已存在的路由条目.如果不存在, 就添加一条. //路由表中的每个槽包含一个路由条目链表, 这是因为可能有冲突的哈希值存在(不同的哈希键, 即目的节点ID不同, 可能有相同的哈希值, 即槽号相同). //为在哈希表中添加一个路由条目: //首先使用哈希函数makehash()获得这个路由条目应被保存的槽号. //然后将路由条目附加到该槽的链表中. void routingtable_setnextnode(routingtable_t* routingtable, int destNodeID, int nextNodeID) { int pos = makehash(destNodeID); routingtable_entry_t *table; if(routingtable->hash[pos] == NULL) { //add routingtable->hash[pos] = (routingtable_entry_t *)malloc(sizeof(routingtable_entry_t)); table = routingtable->hash[pos]; table->destNodeID = destNodeID; table->nextNodeID = nextNodeID; table->next = NULL; return; } else { //update! table = routingtable->hash[pos]; while(table!= NULL) { if(table->destNodeID == destNodeID) { table->nextNodeID = nextNodeID; return; } table = table->next; } table = (routingtable_entry_t*)malloc(sizeof(routingtable_entry_t)); table->next = routingtable->hash[pos]; routingtable->hash[pos] = table; } return; }
static void update_inohash(struct cached_page *hb, ino_t ino, u64_t ino_off) { assert(ino != VMC_NO_INODE); if(hb->ino != VMC_NO_INODE) { int h = makehash(hb->ino, hb->ino_offset); rmhash_byino(hb, &cache_hash_byino[h]); } hb->ino = ino; hb->ino_offset = ino_off; addcache_byino(hb); }
//这个函数在路由表中查找指定的目标节点ID. //为找到一个目的节点的路由条目, 你应该首先使用哈希函数makehash()获得槽号, //然后遍历该槽中的链表以搜索路由条目.如果发现destNodeID, 就返回针对这个目的节点的下一跳节点ID, 否则返回-1. int routingtable_getnextnode(routingtable_t* routingtable, int destNodeID) { int index = makehash(destNodeID); routingtable_entry_t *temp = routingtable->hash[index]; while(temp != NULL){ if(temp->destNodeID == destNodeID){ return temp->nextNodeID; } temp = temp->next; } return -1; }
struct cached_page *find_cached_page_byino(dev_t dev, ino_t ino, u64_t ino_off, int touchlru) { struct cached_page *hb; assert(ino != VMC_NO_INODE); assert(dev != NO_DEV); for(hb = cache_hash_byino[makehash(ino, ino_off)]; hb; hb=hb->hash_next_ino) { if(hb->dev == dev && hb->ino == ino && hb->ino_offset == ino_off) { if(touchlru) cache_lru_touch(hb); return hb; } } return NULL; }
//这个函数在路由表中查找指定的目标节点ID. //为找到一个目的节点的路由条目, 你应该首先使用哈希函数makehash()获得槽号, //然后遍历该槽中的链表以搜索路由条目.如果发现destNodeID, 就返回针对这个目的节点的下一跳节点ID, 否则返回-1. int routingtable_getnextnode(routingtable_t* routingtable, int destNodeID) { int pos = makehash(destNodeID); routingtable_entry_t *table; if(routingtable->hash[pos] == NULL) { return -1; } else { table = routingtable->hash[pos]; while(table != NULL) { if(table->destNodeID == destNodeID) { return table->nextNodeID; } table = table->next; } } return -1; }
bool hash_exists(hash *hashtable[], bstring key, unsigned int tablesize) { hash *tmp = NULL; unsigned int thehash = makehash(key, tablesize); if (biseq(hashtable[thehash]->key, key) == 1) { return true; } else { /* traverse through the linked list (collision resolution) */ for (tmp = hashtable[thehash]; tmp != NULL; tmp = tmp->next) if (biseq(tmp->key, key) == 1 && tmp->value != NULL) return true; } return false; }
//This function looks up the destNodeID in the routing table. //Since routing table is a hash table, this opeartion has O(1) time complexity. //To find a routing entry for a destination node, you should first use the hash function makehash() to get // the slot number and then go through the linked list in that slot to search the routing entry. //If the destNodeID is found, return the nextNodeID for this destination node. //If the destNodeID is not found, return -1. int routingtable_getnextnode(routingtable_t* routingtable, int destNodeID) { int hashNum = makehash(destNodeID); if (routingtable->hash[hashNum]) { routingtable_entry_t *currentEntry = routingtable->hash[hashNum]; if (currentEntry->destNodeID == destNodeID){ //if it's the entry we want.. return currentEntry->nextNodeID; } while (currentEntry->next != NULL) { //else look through linked list for entry we want currentEntry = currentEntry->next; if (currentEntry->destNodeID == destNodeID){ return currentEntry->nextNodeID; } } return -1; } else { return -1; } }
//这个函数动态创建路由表.表中的所有条目都被初始化为NULL指针. //然后对有直接链路的邻居,使用邻居本身作为下一跳节点创建路由条目,并插入到路由表中. //该函数返回动态创建的路由表结构. routingtable_t* routingtable_create() { routingtable_t *route_table = (routingtable_t *)malloc(sizeof (routingtable_t)); int i = 0; for(i = 0; i < MAX_ROUTINGTABLE_SLOTS; i++) route_table->hash[i] = NULL; int nbrnum = topology_getNbrNum(); int *nbrArray = topology_getNbrArray(); for(i = 0; i < nbrnum; i++){ int nextNodeID = nbrArray[i]; int index = makehash(nextNodeID); routingtable_entry_t *temp = route_table->hash[index]; routingtable_entry_t *new_entry = (routingtable_entry_t *)malloc(sizeof(routingtable_entry_t)); new_entry->destNodeID = nextNodeID; new_entry->nextNodeID = nextNodeID; new_entry->next = temp; route_table->hash[index] = new_entry; } return route_table; }
struct cached_page * find_cached_page_bydev(dev_t dev, u64_t dev_off, ino_t ino, u64_t ino_off, int touchlru) { struct cached_page *hb; for(hb = cache_hash_bydev[makehash(dev, dev_off)]; hb; hb=hb->hash_next_dev) { if(hb->dev == dev && hb->dev_offset == dev_off) { if(ino != VMC_NO_INODE) { if(hb->ino != ino || hb->ino_offset != ino_off) { update_inohash(hb, ino, ino_off); } } if(touchlru) cache_lru_touch(hb); return hb; } } return NULL; }
int addcache(dev_t dev, u64_t dev_off, ino_t ino, u64_t ino_off, struct phys_block *pb) { int hv_dev; struct cached_page *hb; if(pb->flags & PBF_INCACHE) { printf("VM: already in cache\n"); return EINVAL; } if(!SLABALLOC(hb)) { printf("VM: no memory for cache node\n"); return ENOMEM; } assert(dev != NO_DEV); #if CACHE_SANITY assert(!find_cached_page_bydev(dev, dev_off, ino, ino_off)); #endif hb->dev = dev; hb->dev_offset = dev_off; hb->ino = ino; hb->ino_offset = ino_off; hb->page = pb; hb->page->refcount++; /* block also referenced by cache now */ hb->page->flags |= PBF_INCACHE; hv_dev = makehash(dev, dev_off); hb->hash_next_dev = cache_hash_bydev[hv_dev]; cache_hash_bydev[hv_dev] = hb; if(hb->ino != VMC_NO_INODE) addcache_byino(hb); lru_add(hb); return OK; }
//这个函数使用给定的目的节点ID和下一跳节点ID更新路由表. //如果给定目的节点的路由条目已经存在, 就更新已存在的路由条目.如果不存在, 就添加一条. //路由表中的每个槽包含一个路由条目链表, 这是因为可能有冲突的哈希值存在(不同的哈希键, 即目的节点ID不同, 可能有相同的哈希值, 即槽号相同). //为在哈希表中添加一个路由条目: //首先使用哈希函数makehash()获得这个路由条目应被保存的槽号. //然后将路由条目附加到该槽的链表中. void routingtable_setnextnode(routingtable_t* routingtable, int destNodeID, int nextNodeID) { int index = makehash(destNodeID); routingtable_entry_t *temp = routingtable->hash[index]; while(temp != NULL){ if(temp->destNodeID == destNodeID){ temp->nextNodeID = nextNodeID; return; } temp = temp->next; } //can not find the item, add it printf("add new route item\n"); temp = (routingtable_entry_t *)malloc(sizeof(routingtable_entry_t)); temp->destNodeID = destNodeID; temp->nextNodeID = nextNodeID; temp->next = routingtable->hash[index]; routingtable->hash[index] = temp; return; }
//这个线程处理来自SON进程的进入报文. 它通过调用son_recvpkt()接收来自SON进程的报文. //如果报文是SIP报文,并且目的节点就是本节点,就转发报文给STCP进程. 如果目的节点不是本节点, //就根据路由表转发报文给下一跳.如果报文是路由更新报文,就更新距离矢量表和路由表. void* pkthandler(void* arg) { //你需要编写这里的代码. printf("we are now in pkthandler in sip!\n"); sip_pkt_t pkt; int slot_num; routingtable_entry_t *ptr; int myID = topology_getMyNodeID(); while(son_recvpkt(&pkt,son_conn) > 0) { if (pkt.header.type == SIP) { printf("Routing: received a packet from neighbor %d\n",pkt.header.src_nodeID); //printf("---the received dest_nodeID is %d\n", pkt.header.dest_nodeID); //printf("---the received length is %d\n", pkt.header.length); //printf("---the received type is SIP\n"); if (pkt.header.dest_nodeID == myID) { forwardsegToSTCP(stcp_conn, pkt.header.src_nodeID, (seg_t*)pkt.data); switch(((seg_t*)(pkt.data))->header.type) { case 0: printf("SIP forward up the seg --SYN-- to the STCP!\n"); break; case 1: printf("SIP forward up the seg --SYNACK-- to the STCP!\n"); break; case 2: printf("SIP forward up the seg --FIN-- to the STCP!\n"); break; case 3: printf("SIP forward up the seg --FINACK-- to the STCP!\n"); break; case 4: printf("SIP forward up the seg --DATA-- to the STCP! and the service is %d the message is %s\n", ((struct packet_IM*)(((seg_t*)(pkt.data))->data))->service, ((struct packet_IM*)(((seg_t*)(pkt.data))->data))->message); break; case 5: printf("SIP forward up the seg --DAYAACK-- to the STCP!\n"); break; } } else { slot_num = makehash(pkt.header.dest_nodeID); ptr = routingtable->hash[slot_num]; while (ptr != NULL) {//寻找是否存在给定目的节点的路由条目 if (pkt.header.dest_nodeID == ptr->destNodeID) break; ptr = ptr->next; } if (ptr != NULL) {//根据路由表转发报文给下一跳 son_sendpkt(ptr->nextNodeID, &pkt, son_conn); printf("SIP forward down the seg to the SON, the nextnode%d!\n", ptr->nextNodeID); } } } else if (pkt.header.type == ROUTE_UPDATE) { /*================本实验精华所在================*/ //printf("---the received type is ROUTE_UPDATE\n"); pkt_routeupdate_t *routeUp = (pkt_routeupdate_t *)pkt.data; pthread_mutex_lock(dv_mutex); pthread_mutex_lock(routingtable_mutex); int i, j, srcNode = pkt.header.src_nodeID; int entryNum = topology_getNodeNum(), nbrNum = topology_getNbrNum(); for(j = 0;j < entryNum;j ++) dvtable_setcost(dv, srcNode,routeUp->entry[j].nodeID, routeUp->entry[j].cost); for(i = 0;i < entryNum;i ++) { if(topology_getMyNodeID() == (dv + i)->nodeID) break; } dv_entry_t *myEntry = (dv + i)->dvEntry; int *nbr = topology_getNbrArray(); for(i = 0;i < entryNum;i ++) { srcNode = topology_getMyNodeID(); int destNode = (myEntry + i)->nodeID; for(j = 0;j < nbrNum;j ++) { int midNode = *(nbr + j); int firstCost = nbrcosttable_getcost(nct, midNode); int lastCost = dvtable_getcost(dv, midNode, destNode); if(firstCost + lastCost < dvtable_getcost(dv, srcNode, destNode)) { dvtable_setcost(dv, srcNode, destNode, firstCost + lastCost); routingtable_setnextnode(routingtable, destNode, midNode); } } } pthread_mutex_unlock(routingtable_mutex); pthread_mutex_unlock(dv_mutex); } else { printf("error type\n"); //exit(0); } } close(son_conn); son_conn = -1; printf("we are now off pkthandler!\n"); pthread_exit(NULL); }
void waitSTCP() { //你需要编写这里的代码. //Create a socket for the socket //If sockfd<0 there was an error in the creation of the socket printf("we are now in waitSTCP!\n"); int slot_num; sip_pkt_t* pkt; int myID = topology_getMyNodeID(); int listenfd, connfd; socklen_t clilen; struct sockaddr_in cliaddr, servaddr; if ((listenfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) { perror("Problem in creating the socket"); exit(2); } //preparation of the socket address memset(&servaddr,0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(SIP_PORT); //bind the socket bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)); //listen to the socket by creating a connection queue, then wait for clients listen(listenfd, MAX_NODE_NUM); printf("Server running...waiting for connections.\n"); //accept a connection while(1) { clilen = sizeof(cliaddr); connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen); stcp_conn = connfd; printf("Received request...\n"); seg_t* segPtr = (seg_t*)malloc(sizeof(seg_t)); int* dest_nodeID = (int*)malloc(sizeof(int)); while (getsegToSend(stcp_conn, dest_nodeID, segPtr) > 0) { switch(segPtr->header.type) { case 0: printf("SIP get some seg --SYN-- from STCP %d!\n", stcp_conn); break; case 1: printf("SIP get some seg --SYNACK-- from STCP %d!\n", stcp_conn); break; case 2: printf("SIP get some seg --FIN-- from STCP %d!\n", stcp_conn); break; case 3: printf("SIP get some seg --FINACK-- from STCP %d!\n", stcp_conn); break; case 4: printf("SIP get some seg --DATA-- from STCP to send! and the service is %d, and the message is %s\n", ((struct packet_IM*)(segPtr->data))->service, ((struct packet_IM*)(segPtr->data))->message); break; case 5: printf("SIP get some seg --DAYAACK-- from STCP to send!\n"); break; } slot_num = makehash(*dest_nodeID); routingtable_entry_t* ptr = routingtable->hash[slot_num]; while (ptr != NULL) {//寻找是否存在给定目的节点的路由条目 if (*dest_nodeID == ptr->destNodeID) break; ptr = ptr->next; } if (ptr != NULL) {//根据路由表转发报文给下一跳 pkt = (sip_pkt_t*)malloc(sizeof(sip_pkt_t)); pkt->header.src_nodeID = myID; pkt->header.dest_nodeID = *dest_nodeID; pkt->header.length = 24 + segPtr->header.length; pkt->header.type = SIP;//包含在报文中的数据是一个STCP段(包括段首部和数据)——Hobo memcpy(pkt->data, segPtr, pkt->header.length); son_sendpkt(ptr->nextNodeID, pkt, son_conn); printf("sip has send pkt to the son %d %d and the service is %d\n", ptr->nextNodeID, son_conn, ((struct packet_IM*)(segPtr->data))->service); } else { printf("can not get the destnodeID!\n"); //exit(0); } } } printf("we are now off waitSTCP!\n"); return; }
void main(int argc,char **argv) { char *def; char *local; const char *action = ""; char *psz; const char *err; int fd; unsigned int i,j; int flagremote; int match; int goodexit = 0; /* exit code for normal exit */ /* in manager this will be set to 0 */ unsigned long from,u,to,issue,prevmax; unsigned long mno = 0; unsigned long chunk; unsigned long subs = 0; unsigned int pos,pos1; unsigned int len; int opt; char outformat = 0; msgentry *msgtable; subentry *subtable; authentry *authtable; dateentry *datetable; struct datetime dt; char date[DATE822FMT]; (void) umask(022); sig_pipeignore(); when = now(); datetime_tai(&dt,when); opt = getconfopt(argc,argv,options,1,0); initsub(0); if (flagformat != 0) if (FORMATS[str_chr(FORMATS,flagformat[0])]) outformat = flagformat[0]; if (outformat == 0) { outformat = (getconf_line(&line,"digformat",0) && FORMATS[str_chr(FORMATS,line.s[0])]) ? line.s[0] : DEFAULT_FORMAT; } /* code to activate digest (-digest-code)*/ if ((digestcode = argv[opt]) == 0) { if (getconf_line(&digestcodefile,"digestcode",0) && digestcodefile.len > 0) { if (!stralloc_0(&digestcodefile)) die_nomem(); digestcode = digestcodefile.s; } } /* ignore any extra args */ if (!stralloc_copy(&subject,&outlocal)) die_nomem(); /* for subjects */ if (!stralloc_copy(&listname,&outlocal)) die_nomem(); /* for content disp */ local = env_get("LOCAL"); def = env_get("DEFAULT"); sender = get_sender(); if (local && *local) { /* in editor local = outlocal */ if (!sender) strerr_die2x(100,FATAL,MSG(ERR_NOSENDER)); if (!*sender) strerr_die2x(100,FATAL,MSG(ERR_BOUNCE)); if (str_equal(sender,"#@[]")) strerr_die2x(100,FATAL,MSG(ERR_BOUNCE)); if (!sender[str_chr(sender,'@')]) strerr_die2x(100,FATAL,MSG(ERR_ANONYMOUS)); if (def) { if (*def) { action = def; goodexit = 99; } else _exit(0); /* list-@host should do -help from manager */ } else { /* editor */ act = AC_DIGEST; /* on list-@host ! */ flageditor = 1; /* to avoid Mailing-list error on sublists */ /* when running out of dir/editor. */ } if (case_starts(action,"dig")) { action += 3; if (action[0] == '-' || action [0] == '.') { action++; if (!digestcode) strerr_die2x(100,FATAL,MSG(ERR_BAD_DIGCODE)); len = str_len(digestcode); if (len <= str_len(action) && case_startb(action,len,digestcode)) { if (FORMATS[str_chr(FORMATS,*(action+len))]) outformat = *(action+len); act = AC_DIGEST; } else strerr_die2x(100,FATAL,MSG(ERR_BAD_DIGCODE)); } } } else /* Command line operation */ act = AC_DIGEST; /* Things we deal with. If anything else just die with success! */ /* At the moment this is -index, -thread, and -get. */ /* If flagdo = 0 we only service -dig commands. This is to support*/ /* "secret" lists that are still archived and digested. -c on */ /* cmd line. */ if (act == AC_NONE) { if (case_equals(action,ACTION_DIGEST)) { act = AC_GET; /* list-digest@ => msg since last digest */ action = ACTION_GET; } else if (case_starts(action,ACTION_GET) || case_starts(action,ALT_GET)) act = AC_GET; else if (case_starts(action,ACTION_INDEX) || case_starts(action,ALT_INDEX)) act = AC_INDEX; else if (case_starts(action,ACTION_THREAD) || case_starts(action,ALT_THREAD)) act = AC_THREAD; } if (act == AC_NONE) /* not for us. Pass the buck. */ _exit(0); if (act != AC_INDEX) { /* need to do header processing */ if(!getconf(&digheaders,"digheaders",0)) { if(!stralloc_copys(&digheaders,digsz)) die_nomem(); if (!stralloc_0(&digheaders)) die_nomem(); psz = digheaders.s; while (*psz) { if (*psz == '\\') *psz = '\0'; ++psz; } } if (!constmap_init(&digheadersmap,digheaders.s,digheaders.len,0)) die_nomem(); } if (act != AC_DIGEST) { if (!flagdo) /* only do digests */ strerr_die2x(100,FATAL,MSG(ERR_NOCMD)); if (flagpublic < 0) flagpublic = !getconf_isset("modgetonly") && getconf_isset("public"); if (!flagpublic) { /* This all to take care of non-public lists. They should*/ /* still do digests, but do other things only for */ /* moderators that have remote access. Since this is rare*/ /* efforts have been made to keep everything that's not */ /* needed elsewhere in here. */ getconf_line(&moddir,"modsub",0); flagremote = getconf_line(&line,"remote",0); if (!flagremote) strerr_die2x(100,FATAL,MSG(ERR_NOT_PUBLIC)); if (!moddir.len) { if (line.len) { if (!stralloc_copy(&moddir,&line)) die_nomem(); } else { if (!stralloc_copys(&moddir,"mod")) die_nomem(); } } if (!stralloc_0(&moddir)) die_nomem(); ismod = issub(moddir.s,sender,&mod); if (!ismod) /* sender = moderator? */ strerr_die2x(100,FATAL,MSG(ERR_NOT_PUBLIC)); } } if (act == AC_DIGEST) { workdir = "digest"; if (!stralloc_cats(&outlocal,"-digest")) die_nomem(); if (getconf_line(&line,"chunk",0)) { if (!stralloc_0(&line)) die_nomem(); (void) scan_ulong(line.s,&chunk); /* same chunk as main list */ if (chunk == 0) /* limit range to 1-53 */ chunk = 1L; else if (chunk > 52) chunk = 52L; } else { chunk = 0L; } } else workdir = "."; if (!flagarchived) strerr_die2x(100,FATAL,MSG(ERR_NOT_ARCHIVED)); if (qmail_open(&qq) == -1) strerr_die2sys(111,FATAL,MSG(ERR_QMAIL_QUEUE)); set_cpnum(""); /* default for <#n#> replacement */ switch (act) { case AC_DIGEST: /* -dig{.|-}'digestcode'[f] returns an rfc1153 digest */ /* of messages from the archive. Messages */ /* dignum+1 through the last message received by the list are processed and */ /* dignum is updated to the last message processed. digissue is advanced. */ get_num(); /* max = last successful message */ to = max; lockup(); /* another digest could corrupt dignum */ /* but will be saved only if flagdigrange==0 */ if(getconf_line(&num,"dignum",0)) { if(!stralloc_0(&num)) die_nomem(); pos = scan_ulong(num.s,&prevmax); if (num.s[pos] == ':') pos++; pos += 1 + scan_ulong(num.s+pos,&cumsize); /* last cumsize */ if (num.s[pos] == ':') pos++; scan_ulong(num.s+pos,&digwhen); /* last reg dig */ } else { prevmax = 0L; cumsize = 0L; digwhen = 0L; } mno = prevmax + 1L; if(!max || mno > max) /* if a digest-list is "sending" the request, */ /* don't make noise: errors go to postmaster!*/ strerr_die2x(goodexit,FATAL,MSG(ERR_EMPTY_DIGEST)); szmsgnum[fmt_ulong(szmsgnum,mno)] = '\0'; set_cpnum(szmsgnum); /* for copy */ /* prepare subject to get entropy for tagmsg*/ if (!stralloc_cats(&subject," Digest ")) die_nomem(); if (!stralloc_catb(&subject,date,date822fmt(date,&dt)-1)) die_nomem(); /* skip trailing in date '\n' */ if (!stralloc_cats(&subject," Issue ")) die_nomem(); if (getconf_line(&num,"digissue",0)) { if(!stralloc_0(&num)) die_nomem(); scan_ulong(num.s,&issue); issue++; } else { issue = 1; } if (!stralloc_catb(&subject,strnum,fmt_ulong(strnum,issue))) die_nomem(); /* use the subject as entropy */ if (!stralloc_copy(&line,&subject)) die_nomem(); if (!stralloc_0(&line)) die_nomem(); if (!stralloc_ready(&seed,HASHLEN+1)) die_nomem(); seed.len = HASHLEN + 1; seed.s[HASHLEN] = '\0'; makehash(line.s,line.len,seed.s); if (chunk) { /* only if slaves are used */ qmail_puts(&qq,"Ezauth: "); qmail_put(&qq,seed.s,HASHLEN); qmail_puts(&qq,"\n"); } doheaders(); qmail_puts(&qq,"To: "); if (!quote("ed,&listname)) die_nomem(); qmail_put(&qq,quoted.s,quoted.len); qmail_puts(&qq,"@"); qmail_put(&qq,outhost.s,outhost.len); qmail_puts(&qq,"\n"); if (flagindexed && (outformat != NATIVE)) idx_mkthreads(&msgtable,&subtable,&authtable,&datetable, mno,to,max,flaglocked); else idx_mklist(&msgtable,&subtable,&authtable,mno,to); digest(msgtable,subtable,authtable,mno,to,&subject,AC_DIGEST,outformat); write_ulong(issue,0L,0L,"digissue","digissuen"); write_ulong(max,cumsizen, (unsigned long) when,"dignum","dignumn"); break; case AC_GET: /* -get[-|\.][[num].num2] copies archive num-num2. num & num2 are adjusted */ /* to be > 0 and <= last message, to num2 >= num and to num2-num <= MAXGET. */ zapnonsub(ACTION_GET); /* restrict to subs if requested */ tosender(); /* for rfc1153 */ if (!stralloc_cats(&subject," Digest of: ")) die_nomem(); if (!stralloc_cats(&subject,action)) die_nomem(); to = 0; pos = str_len(ACTION_GET); if (!case_starts(action,ACTION_GET)) pos = str_len(ALT_GET); if (FORMATS[str_chr(FORMATS,action[pos])]) { outformat = action[pos]; ++pos; } /* optional - or . after '-get' */ if (action[pos] == '-' || action[pos] == '.') pos++; get_num(); /* max = last successful message */ /* accept any separator. It may be */ /* the terminal '\n', but then */ /* scan will = 0 on the \0 so should*/ /* be safe */ if (!max) strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST)); szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ doheaders(); if(action[pos += scan_ulong(action + pos,&u)]) scan_ulong(action + pos + 1, &to); if (u == 0 && to == 0) { /* default: messages since last */ /* digest, or last MAXGET if too many */ to= max; u = dignum(); if (u == 0) { /* no digest => last up to HISTGET msgs */ to = max; if (max > HISTGET) u = max - HISTGET; else u = 1; } if (to - u >= MAXGET) u = to - MAXGET + 1; /* max MAXGET */ } else if (u > max) { if (to) { /* -get.999999_x returns 30 and msg since last*/ to = max; /* digest 30*/ u = dignum(); if (u > HISTGET) u -= HISTGET; else u = 1; if (to - u >= MAXGET) u = to - MAXGET + 1; } else u = max; } if (u == 0) u = 1; /* -get.5 => 1-5 */ if (to < u) to = u; /* -get23_2 => 23 */ if (to >= u + MAXGET) to = u + MAXGET - 1; /* no more than MAXGET at a time */ if (to > max) to = max; if (flagindexed && (outformat != NATIVE)) /* fake out threading */ idx_mkthreads(&msgtable,&subtable,&authtable,&datetable, u,to,max,0); else idx_mklist(&msgtable,&subtable,&authtable,u,to); digest(msgtable,subtable,authtable,u,to,&subject,AC_GET,outformat); break; case AC_INDEX: /* -index[f][#|-|\.][[num][.num2] Show subject index for messages num-num2 in*/ /* sets of 100. */ /* Default last 2 sets. num and num2 are made reasonable as for get. num2 is */ /* limited to num+MAXINDEX to limit the amount of data sent. */ if (!flagindexed) strerr_die2x(100,FATAL,MSG(ERR_NOT_INDEXED)); zapnonsub(ACTION_INDEX); /* restrict to subs if requested */ to = 0; pos = str_len(ACTION_INDEX); if (!case_starts(action,ACTION_INDEX)) pos = str_len(ALT_INDEX); if (FORMATS[str_chr(FORMATS,action[pos])]) { outformat = action[pos]; /* ignored, but be nice ... */ ++pos; } get_num(); /* max = last successful message */ if (!max) strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST)); szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ doheaders(); tosender(); if (!stralloc_cats(&subject," Result of: ")) die_nomem(); if (!stralloc_cats(&subject,action)) die_nomem(); presub(1,1,&subject,AC_INDEX,outformat); if (action[pos] == '-' || action[pos] == '.') pos++; if(action[pos += scan_ulong(action + pos,&u)]) scan_ulong(action + pos + 1, &to); if (u == 0 && to == 0) { to = max; u = max - 100; } if (u <= 0) u = 1; if (u > max) u = max; if (to < u) to = u; if (to > u + MAXINDEX) to = u+MAXINDEX; /* max MAXINDEX index files */ if (to > max) to = max; u /= 100; to /= 100; while (u <= to) { if (!stralloc_copys(&fn,"archive/")) die_nomem(); if (!stralloc_catb(&fn,strnum,fmt_ulong(strnum,u))) die_nomem(); if (!stralloc_cats(&fn,"/index")) die_nomem(); if (!stralloc_0(&fn)) die_nomem(); if (u == max/100) /* lock if last index file in archive */ lockup(); fd = open_read(fn.s); if (fd == -1) if (errno != error_noent) strerr_die2sys(111,FATAL,MSG1(ERR_OPEN,fn.s)); else code_qputs(MSG(TXT_NOINDEX)); else { substdio_fdbuf(&sstext,read,fd,textbuf,sizeof(textbuf)); for (;;) { if (getln(&sstext,&line,&match,'\n') == -1) strerr_die2sys(111,FATAL,MSG1(ERR_READ,fn.s)); if (match) { if (line.s[0] != '\t') { /* subject line */ pos = byte_chr(line.s,line.len,' '); pos1 = 0; if (pos && pos != line.len && line.s[pos - 1] == ':') pos1 = pos + HASHLEN + 1; /* after hash */ if (pos1 >= line.len) { /* bad! */ pos = 0; pos1 = 0; /* output as is */ } if (!stralloc_copyb(&line2,line.s,pos)) die_nomem(); if (!stralloc_catb(&line2,line.s+pos1,line.len-pos1)) die_nomem(); } else { pos = byte_chr(line.s,line.len,';'); if (pos + HASHLEN + 1 < line.len && pos > 15 && line.s[pos + 1] != ' ') { if (!stralloc_copyb(&line2,line.s,pos - 15)) die_nomem(); pos++; if (!stralloc_catb(&line2,line.s + pos + HASHLEN, line.len - pos - HASHLEN)) die_nomem(); } else /* old format - no author hash */ if (!stralloc_copyb(&line2,line.s,line.len)) die_nomem(); } code_qput(line2.s,line2.len); } else break; } close(fd); } if (u == max/100) /* unlock if last index in archive file */ unlock(); u++; } normal_bottom(outformat); postmsg(outformat); break; case AC_THREAD: /* -thread[f][-|.]num returns messages with subject matching message */ /* 'num' in the subject index. If 'num' is not in[1..last_message] an error */ /* message is returned. */ if (!flagindexed) strerr_die2x(100,FATAL,MSG(ERR_NOT_INDEXED)); zapnonsub(ACTION_THREAD); /* restrict to subs if requested*/ get_num(); /* max = last successful message */ if (!max) strerr_die2x(100,FATAL,MSG(ERR_EMPTY_LIST)); szmsgnum[fmt_ulong(szmsgnum,max)] = '\0'; set_cpnum(szmsgnum); /* for copy this is the latest message arch'd*/ doheaders(); tosender(); /* for rfc1153 */ if (!stralloc_cats(&subject," Digest of: ")) die_nomem(); if (!stralloc_cats(&subject,action)) die_nomem(); to = 0; pos = str_len(ACTION_THREAD); if (!case_starts(action,ACTION_THREAD)) pos = str_len(ALT_THREAD); if (FORMATS[str_chr(FORMATS,action[pos])]) { outformat = action[pos]; ++pos; } if (action[pos] == '-' || action[pos] == '.') pos++; if(action[pos += scan_ulong(action + pos,&u)]) scan_ulong(action + pos + 1, &to); if(u == 0 || u > max) { hdr_add2("Subject: ",subject.s,subject.len); qmail_puts(&qq,"\n"); copy(&qq,"text/get-bad",flagcd); } else { /* limit range to at most u-THREAD_BEFORE to u+THREAD_AFTER */ if (u > THREAD_BEFORE) from = u-THREAD_BEFORE; else from = 1L; if (u + THREAD_AFTER > max) { idx_mkthread(&msgtable,&subtable,&authtable,from,max,u,max,0); digest(msgtable,subtable,authtable,from,max,&subject, AC_THREAD,outformat); } else { idx_mkthread(&msgtable,&subtable,&authtable, from,u+THREAD_AFTER,u,max,0); digest(msgtable,subtable,authtable,from,u+THREAD_AFTER, &subject,AC_THREAD,outformat); } } break; default: /* This happens if the initial check at the beginning of 'main' */ /* matches something that isn't matched here. Conversely, just */ /* adding an action here is not enough - it has to be added to the */ /* initial check as well. */ strerr_die2x(100,FATAL, "Program error: I'm supposed to deal with this but I didn't"); } if (!stralloc_copy(&line,&outlocal)) die_nomem(); if (act == AC_DIGEST) { if (chunk) { if (!stralloc_cats(&line,"-return-g-")) die_nomem(); } else if (!stralloc_cats(&line,"-return-")) die_nomem(); strnum[fmt_ulong(strnum,mno)] = '\0'; if (!stralloc_cats(&line,strnum)) die_nomem(); if (!stralloc_cats(&line,"-@")) die_nomem(); if (!stralloc_cat(&line,&outhost)) die_nomem(); if (!stralloc_cats(&line,"-@[]")) die_nomem(); } else { if (!stralloc_cats(&line,"-return-@")) die_nomem(); if (!stralloc_cat(&line,&outhost)) die_nomem(); } if (!stralloc_0(&line)) die_nomem(); qmail_from(&qq,line.s); if (act == AC_DIGEST) { /* Do recipients */ tagmsg(mno,seed.s,"d",hashout,qq.msgbytes,chunk); if (chunk) { if (!stralloc_copys(&line,"T")) die_nomem(); if (!stralloc_cat(&line,&outlocal)) die_nomem(); if (!stralloc_cats(&line,"-s-d-")) die_nomem(); if (!stralloc_catb(&line,hashout,COOKIE)) die_nomem(); if (!stralloc_cats(&line,"-")) die_nomem(); if (!stralloc_cats(&line,strnum)) die_nomem(); if (!stralloc_cats(&line,"-")) die_nomem(); if (!stralloc_copys(&line2,"@")) die_nomem(); if (!stralloc_cat(&line2,&outhost)) die_nomem(); if (!stralloc_0(&line2)) die_nomem(); j = 0; for (i = 0; i <= 52; i += chunk) { /* To slaves */ qmail_put(&qq,line.s,line.len); schar[0] = '0' + i / 10; schar[1] = '0' + (i % 10); qmail_put(&qq,schar,3); j += (chunk - 1); if (j > 52) j = 52; schar[0] = '0' + j / 10; schar[1] = '0' + (j % 10); qmail_put(&qq,schar,2); qmail_put(&qq,line2.s,line2.len); } } else subs = putsubs(workdir,0L,52L,&subto); } else { /* if local is set, sender is checked */ if (ismod) qmail_to(&qq,mod.s); else qmail_to(&qq,sender); } if (*(err = qmail_close(&qq)) == '\0') { /* Done. Skip rest. */ if (act == AC_DIGEST) { if (chunk) (void) logmsg(mno,0L,0L,2); else (void) logmsg(mno,0L,subs,4); } closesub(); /* close db connection */ unlock(); /* NOP if nothing locked */ strnum[fmt_ulong(strnum,qmail_qp(&qq))] = 0; strerr_die2x(goodexit,"ezmlm-get: info: qp ",strnum); } else { /* failed. Reset last msg & issue for digest */ if(act == AC_DIGEST) { issue--; write_ulong(issue,0L,0L,"digissue","digissuen"); write_ulong(prevmax,cumsize,(unsigned long) digwhen,"dignum","dignumn"); } unlock(); /* NOP if nothing locked */ strerr_die4x(111,FATAL,MSG(ERR_TMP_QMAIL_QUEUE),": ",err + 1); } }