/* ARISA - pool handling functions * Copyright (C) 2003, 2004 Carl Ritson * * 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 */ #include "arisa.h" static const char *err_dup = "Duplicate Send"; static const char *err_max = "At Per User Send Limit"; static const char *err_full = "All Sends Full"; static const char *err_unknown = "Unknown Error"; int pool_add(interface_t *interface, pool_t *p) { int i,ret = -1; LOCK(interface); LOCK(p); for(i = 0; i < interface->send.no_pools; ++i) { LOCK(interface->send.pools[i]); if(strcasecmp(interface->send.pools[i]->name,p->name) == 0 && interface->send.pools[i]->deleted == 0) { LOGP(L_ERR,"Error, attempted to add duplicate pool %s to Interface \"%s\"", interface->send.pools[i]->name, interface->name); UNLOCK(interface->send.pools[i]); break; } UNLOCK(interface->send.pools[i]); } if(i == interface->send.no_pools) { LOGP(L_INF,"Added pool %s to Interface \"%s\"", p->name, interface->name); PTRARR_ADD(&interface->send.pools,&interface->send.no_pools,p); validity_pair_insert(interface,p); ret = 0; } UNLOCK(p); UNLOCK(interface); if(ret == 0) se_notify_pool_add(interface,p); return ret; } void pool_purge(interface_t *interface, const time_t now) { pool_t **old = NULL; int no_old = 0; int i,j,deleted; LOCK(interface); for(i = 0, deleted = 0; i < interface->send.no_pools; ++i) { LOCK(interface->send.pools[i]); if(IS_PURGE_TIME(interface->send.pools[i]->deleted,now) && interface->send.pools[i]->no_csends == 0) deleted++; UNLOCK(interface->send.pools[i]); } if(deleted) { old = interface->send.pools; no_old = interface->send.no_pools; interface->send.pools = xalloc(sizeof(pool_t *) * (no_old - deleted)); interface->send.no_pools= (no_old - deleted); for(i = 0, j = 0; i < no_old; ++i) { LOCK(old[i]); if(!IS_PURGE_TIME(old[i]->deleted,now)) { interface->send.pools[j++] = old[i]; UNLOCK(old[i]); old[i] = NULL; } else UNLOCK(old[i]); } } UNLOCK(interface); if(deleted) { for(i = 0; i < no_old; ++i) { if(old[i] != NULL) free_pool(old[i]); } } if(old != NULL) xfree(old); } int pool_del(interface_t *interface, pool_t *p) { int i,okay = 1; LOCK(global); for(i = 0; i < global->no_queues && okay; ++i) { LOCK(global->queues[i]); if(global->queues[i]->deleted == 0 && global->queues[i]->pool == p) { LOGP(L_ERR,"Error trying to remove pool with queues attached"); okay = 0; } UNLOCK(global->queues[i]); } if(okay) { LOCK(interface); LOCK(p); LOGP(L_INF,"Deleted pool %s",p->name); p->deleted = xtime(); validity_delete(p); for(i = 0; i < p->no_csends; ++i) { LOCK(p->csends[i]); p->csends[i]->state = STATE_DELETED; UNLOCK(p->csends[i]); } UNLOCK(p); UNLOCK(interface); UNLOCK(global); return 0; } else { UNLOCK(global); return -1; } } pool_t *pool_find(interface_t *interface, const char *name) { pool_t *ret = NULL; int i; LOCK(interface); if(interface->type != INTERFACE_SEND) { UNLOCK(interface); return NULL; } for(i = 0; i < interface->send.no_pools && ret == NULL; ++i) { pool_t *p = interface->send.pools[i]; LOCK(p); if(strcasecmp(p->name,name) == 0 && p->deleted == 0) { ret = p; validity_pair_insert(interface,ret); } UNLOCK(p); } UNLOCK(interface); return ret; } int pool_add_send(pool_t *p, send_t *s, int flags, char **err) { int i,j,ret = 0; if(err != NULL) *err = (char *)err_unknown; LOCK(p); if(p->deleted != 0) { UNLOCK(p); return -1; } LOCK(s); if(p->sends_per_user > 0 && !(flags & SEND_NO_ULIMIT)) { for(i = 0, j = 0; i < p->no_csends && ret == 0; ++i) { LOCK(p->csends[i]); if(p->csends[i]->network == s->network && irc_strcasecmp(p->csends[i]->nick, s->nick) == 0) { if(p->csends[i]->pack == s->pack) ret = -1; j++; } UNLOCK(p->csends[i]); } if(ret == -1) { if(err != NULL) *err = (char *)err_dup; } else if(j >= p->sends_per_user) { if(err != NULL) *err = (char *)err_max; ret = -1; } } if(ret != -1) { if(p->no_csends < p->sends || (flags & SEND_NO_CLIMIT)) { if(err != NULL) *err = NULL; PTRARR_ADD(&p->csends,&p->no_csends,s); s->state = STATE_QUEUED; s->pool = p; ret = 0; } else { if(err != NULL) *err = (char *)err_full; ret = -1; } } UNLOCK(s); UNLOCK(p); return ret; } /* int pool_close_send_no(pool_t *p, const int no) { int ret = -1; LOCK(p); if(no <= p->no_csends && no > 0) { LOCK(p->csends[no]); p->csends[no]->state = STATE_DELETED; UNLOCK(p->csends[no]); ret = 0; } UNLOCK(p); return ret; } */ void pool_purge_sends(pool_t *p) { send_t **old_sends = NULL; int i,j,k,no_old_sends = 0; //D("p: %p",p); LOCK(p); for(j = 0,k = 0; j < p->no_csends; ++j) { LOCK(p->csends[j]); if(p->csends[j]->state == STATE_PURGING || p->csends[j]->state == STATE_PURGEREQUEUE) k++; UNLOCK(p->csends[j]); } //D("%d to purge/requeue",k); if(k > 0) { old_sends = p->csends; no_old_sends = p->no_csends; PTRARR_ALLOC(&(p->csends),&(p->no_csends),no_old_sends - k); for(j = 0, k = 0; j < no_old_sends; ++j) { LOCK(old_sends[j]); if(old_sends[j]->state != STATE_PURGING && old_sends[j]->state != STATE_PURGEREQUEUE) { p->csends[k++] = old_sends[j]; UNLOCK(old_sends[j]); old_sends[j] = NULL; } } } UNLOCK(p); if(old_sends != NULL) { for(i = 0; i < no_old_sends; ++i) { if(old_sends[i] != NULL) { // requeue, requeuable sends if(old_sends[i]->state == STATE_PURGEREQUEUE) { UNLOCK(old_sends[i]); if(queue_requeue(old_sends[i]) != 0) free_send(old_sends[i]); } else { UNLOCK(old_sends[i]); free_send(old_sends[i]); } } } xfree(old_sends); } } /* send is assumed to be locked externally to this function if required. */ int send_matchs(send_t *s, const char *host, network_t *net, const char *nick, const char *file, const char *label) { int ret = 1; if(s->state == STATE_COMPLETE || s->state == STATE_DELETED || s->state == STATE_ERROR) return 0; if(host != NULL) { if(s->host != NULL) ret &= irc_hostmask_match(s->host,host); else ret = 0; } if(net != NULL) ret &= (s->network == net); if(nick != NULL) { if(s->nick != NULL) ret &= (irc_strcasecmp(s->nick,nick) == 0); else ret = 0; } if(file != NULL) { char buffer[256]; RLOCK(s->pack); pathtofn(s->pack->file,buffer,sizeof(buffer)); RUNLOCK(s->pack); ret &= (strcmp(buffer,file) == 0); } if(label != NULL) { RLOCK(s->pack); if(s->pack->label != NULL) { char buffer[512]; colourise(COLOUR_NONE,buffer,sizeof(buffer), s->pack->label); ret &= (strcasecmp(buffer,label) == 0); } else ret = 0; RUNLOCK(s->pack); } return (ret != 0) ? 1 : 0; } void pool_nick_change(pool_t *p, network_t *net, const char *onick, const char *nnick) { int i; LOCK(p); for(i = 0; i < p->no_csends; ++i) { send_t *s = p->csends[i]; LOCK(s); if(s->network == net && irc_strcasecmp(s->nick,onick) == 0) { xfree(s->nick); s->nick = xstrdup(nnick); } UNLOCK(s); } UNLOCK(p); } int pool_rename(interface_t *in, pool_t *p, const char *name) { int i,j, ret = 0; if(p == NULL || name == NULL) return -1; if(in == NULL) { LOCK(global); for(i = 0; i < global->no_interfaces && in == NULL; ++i) { LOCK(global->interfaces[i]); if(global->interfaces[i]->type == INTERFACE_SEND && global->interfaces[i]->deleted == 0) { for(j = 0; j < global->interfaces[i]->send.no_pools && in == NULL; ++j) { if(global->interfaces[i]->send.pools[j] == p) in = global->interfaces[i]; } } UNLOCK(global->interfaces[i]); } if(in != NULL) LOCK(in); UNLOCK(global); if(in == NULL) return -1; } else LOCK(in); if(in->type != INTERFACE_SEND || in->deleted != 0) { UNLOCK(in); return -1; } for(i = 0; i < in->send.no_pools && ret == 0; ++i) { LOCK(in->send.pools[i]); if(in->send.pools[i]->deleted == 0 && strcmp(in->send.pools[i]->name,name) == 0) ret = -1; UNLOCK(in->send.pools[i]); } if(ret == 0) { LOCK(p); xfree(p->name); p->name = xstrdup(name); UNLOCK(p); } UNLOCK(in); return ret; } int pool_valid(interface_t *in, pool_t *p) { if(in == NULL || p == NULL) return 0; if(validity_pair_test(in,p)) return 1; if(interface_valid(in)) { int i; LOCK(in); if(in->type != INTERFACE_SEND || in->deleted != 0) { UNLOCK(in); return 0; } for(i = 0; i < in->send.no_pools; ++i) { if(in->send.pools[i] == p) { LOCK(p); i = p->deleted; if(p->deleted == 0) validity_pair_insert(in,p); UNLOCK(p); UNLOCK(in); if(i == 0) // p->deleted == 0 return 1; else return 0; } } UNLOCK(in); } return 0; } int pool_send_close(pool_t *p, const char *hostmask, network_t *net, const char *nick, const char *file, send_t *ptr, const char *reason) { int i,count; LOCK(p); for(i = 0,count = 0; i < p->no_csends; ++i) { send_t *s = p->csends[i]; LOCK(s); if((ptr == NULL && send_matchs(s,hostmask,net,nick,file,NULL)) || s == ptr) { // If a send is QUEUED, then it has not been picked up // by the interface and should be directly PURGED. switch(s->state) { case STATE_QUEUED: s->state = STATE_PURGING; break; case STATE_PURGING: case STATE_PURGEREQUEUE: s->retries = 0; break; default: s->state = STATE_DELETED; break; } if(s->reason != NULL) xfree(s->reason); s->reason = reason != NULL ? xstrdup(reason) : NULL; count++; } UNLOCK(p->csends[i]); } UNLOCK(p); return count; } int pool_reset_counters(pool_t *p) { LOCK(p); p->record_send = 0; p->record_rate = 0; p->stat_mib = 0; p->stat_carry = 0; UNLOCK(p); return 0; } /** Settings Data **/ static setting_t pool_data[] = { {"sends", ST_UINT,OFFSETOF(pool_t,sends), 0,"0",NULL,NULL,NULL}, {"bandwidth", ST_UINT,OFFSETOF(pool_t,bandwidth), 0,"0",NULL,NULL,NULL}, {"send-max-bandwidth", ST_UINT,OFFSETOF(pool_t,send_max_bandwidth), 0,"0",NULL,NULL,NULL}, {"send-min-bandwidth", ST_UINT,OFFSETOF(pool_t,send_min_bandwidth), 0,"0",NULL,NULL,NULL}, {"sends-per-user", ST_INT, OFFSETOF(pool_t,sends_per_user), 0,"0",NULL,NULL,NULL}, {"dequeue-algorithm", ST_INT, OFFSETOF(pool_t,dequeue_algorithm), 0,"0","2",NULL,NULL}, {NULL} }; int pool_apply_setting(pool_t *p, const char *name, const char *value) { value_t *v = value_string(name,value); int ret; LOCK(p); ret = apply_setting(pool_data,p,v); UNLOCK(p); xfree(v); return ret; } int pool_read_settings(pool_t *p, pqueue_t *out) { int ret; LOCK(p); ret = read_settings(pool_data,p,out); UNLOCK(p); return ret; }