/* ARISA - Scripting Engine * 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 */ #define _ARISA_SCRIPT_ENG_C #include "arisa.h" #include "user.h" #include #include #include #include #include #include #include #include #include #include #include "script-eng.h" #ifdef USE_PERL #include "perl/engine.h" #endif // Static Data // No need to locking on these, as they are used by only 1 thread, // and generally only during the pre-multithread stage of operation. static se_save_data_t **load_cache = NULL; static int no_load_cache = 0; // Functions static se_msg_t *alloc_msg(void) { se_msg_t *m = xalloc(sizeof(se_msg_t)); LOCK_INIT(&(m->lock)); m->type = SE_UNKNOWN; m->occured = xtime(); m->ref_count = 1; return m; } void free_msg(se_msg_t *m) { LOCK(m); m->ref_count--; if(m->ref_count > 0) { UNLOCK(m); return; } else UNLOCK(m); LOCK_FREE(&(m->lock)); switch(m->type) { case SE_LOAD: case SE_UNLOAD: case SE_RELOAD: if(m->script.name != NULL) xfree(m->script.name); break; case SE_IRC_MSG: if(m->irc.data != NULL) strarr_free(&m->irc.data,&m->irc.no_data); break; case SE_SEND_NOTIFICATION: case SE_RECV_NOTIFICATION: case SE_QUEUE_ENQUEUE: case SE_QUEUE_DEQUEUE: if(m->notification.send != NULL) free_send(m->notification.send); break; case SE_PACK_ADD: case SE_PACK_DEL: case SE_PACK_HASHED: case SE_PACKLIST_ADD: if(m->pack.pack != NULL) free_pack(m->pack.pack); break; case SE_SIGNAL: if(m->signal.name != NULL) xfree(m->signal.name); break; case SE_SAVE_DATA: LOCK_FREE(&(m->save.lock)); pthread_cond_destroy(&(m->save.cond)); if(m->save.data != NULL) xfree(m->save.data); default: break; } xfree(m); } static se_msg_t *duplicate_msg(se_msg_t *m) { if(m == NULL) return NULL; LOCK(m); m->ref_count++; UNLOCK(m); return m; } static int dispatch_msg(se_msg_t *msg) { int i,ret; LOCKR(&global->se.lock); for(i = 0,ret = 0; i < global->se.no_interpreters; ++i) { LOCK(global->se.interpreters[i]); pqueue_push_back(&global->se.interpreters[i]->msgqueue, duplicate_msg(msg)); ret++; UNLOCK(global->se.interpreters[i]); } UNLOCKR(&global->se.lock); free_msg(msg); return ret; } int se_register_interpreter(interpreter_t *inp) { int pre,post; LOCKR(&global->se.lock); pre = global->se.no_interpreters; PTRARR_ADD(&global->se.interpreters,&global->se.no_interpreters,inp); post = global->se.no_interpreters; UNLOCKR(&global->se.lock); if(pre != post) { // did it get added it int i; LOCK(inp); // scan the load cache for(i = 0; i < no_load_cache; ++i) { if((inp->flags & SE_F_LOAD_SAVE) && strcmp(load_cache[i]->name,inp->name) == 0) { se_msg_t *m = alloc_msg(); m->type = SE_LOAD_DATA; m->load.data = load_cache[i]->data; m->load.size = load_cache[i]->size; xfree(load_cache[i]->name); xfree(load_cache[i]); PTRARR_DEL(&load_cache,&no_load_cache, load_cache[i]); pqueue_push_back(&inp->msgqueue,m); break; } } thread_create(&(inp->thread),inp->func,inp); UNLOCK(inp); return 0; } else return -1; } static int deliver_to_script_holder(se_msg_t *msg) { interpreter_t *inp; int i,j,ret = -1; LOCKR(&global->se.lock); for(i = 0; i < global->se.no_interpreters && ret == -1; ++i) { inp = global->se.interpreters[i]; LOCK(inp); for(j = 0; j < inp->no_loaded && ret == -1; ++j) { if(strcmp(inp->loaded[j],msg->script.name) == 0) { pqueue_push_back(&inp->msgqueue, duplicate_msg(msg)); ret = 0; } } UNLOCK(inp); } UNLOCKR(&global->se.lock); free_msg(msg); return ret; } int se_load_script(const char *script) { interpreter_t *inp; se_msg_t *m = alloc_msg(); int i,j,ret = -1; m->type = SE_LOAD; m->script.name = xstrdup(script); LOCKR(&global->se.lock); for(i = 0; i < global->se.no_interpreters && ret == -1; ++i) { inp = global->se.interpreters[i]; LOCK(inp); for(j = 0; j < inp->no_types; ++j) { D(""); if(fnmatch(inp->types[j],script,0) == 0) { D(""); pqueue_push_back(&inp->msgqueue,m); ret = 0; } } UNLOCK(inp); } UNLOCKR(&global->se.lock); if(ret == -1) free_msg(m); return ret; } int se_reload_script(const char *script) { se_msg_t *m = alloc_msg(); m->type = SE_RELOAD; m->script.name = xstrdup(script); return deliver_to_script_holder(m); } int se_unload_script(const char *script) { se_msg_t *m = alloc_msg(); m->type = SE_UNLOAD; m->script.name = xstrdup(script); return deliver_to_script_holder(m); } void se_global_start(void) { int i; #ifdef USE_PERL perl_register(); #endif //python_register(); // any unclaimed load structures are now redundant if(load_cache != NULL) { for(i = 0; i < no_load_cache; ++i) { xfree(load_cache[i]->name); if(load_cache[i]->data != NULL) xfree(load_cache[i]->data); xfree(load_cache[i]); } xfree(load_cache); load_cache = NULL; no_load_cache = 0; } LOCK(global); for(i = 0; i < global->settings->no_scripts; ++i) se_load_script(global->settings->scripts[i]); UNLOCK(global); } void se_global_shutdown(pqueue_t *threads) { se_msg_t *m = alloc_msg(); int i; m->type = SE_SHUTDOWN; dispatch_msg(m); LOCKR(&global->se.lock); for(i = 0; i < global->se.no_interpreters; ++i) { interpreter_t *intp = global->se.interpreters[i]; LOCK(intp); pqueue_push_back(threads,&(intp->thread)); thread_end(&(intp->thread)); UNLOCK(intp); } UNLOCKR(&global->se.lock); } void se_irc_msg(network_t *network, char **data, int no_data) { se_msg_t *m = alloc_msg(); m->type = SE_IRC_MSG; m->irc.network = network; m->irc.data = strarr_dup(data,no_data,&m->irc.no_data); dispatch_msg(m); } static send_t *clone_send(send_t *s) { send_t *c = xalloc(sizeof(send_t)); memcpy(c,s,sizeof(send_t)); LOCK_INIT(&(c->lock)); if(c->nick != NULL) c->nick = xstrdup(c->nick); if(c->host != NULL) c->host = xstrdup(c->host); if(c->pack != NULL) pack_ref(c->pack); if(c->reason != NULL) c->reason = xstrdup(c->reason); c->aux = NULL; return c; } static void se_notification(se_msg_type_t type, int dolock, interface_t *intf, queue_t *queue, send_t *s, int aux) { se_msg_t *m = alloc_msg(); m->type = type; if(dolock) LOCK(s); m->notification.interface = intf; m->notification.pool = s->pool; m->notification.queue = queue; m->notification.send = clone_send(s); m->notification.aux = aux; if(dolock) UNLOCK(s); dispatch_msg(m); } void se_notify_send(int dolock, interface_t *intf, send_t *s, int err) { se_notification(SE_SEND_NOTIFICATION, dolock, intf, NULL, s, err); } void se_notify_recv(int dolock, interface_t *intf, send_t *s, int err) { se_notification(SE_RECV_NOTIFICATION, dolock, intf, NULL, s, err); } void se_notify_enqueue(int dolock, queue_t *queue, send_t *s, int pos) { se_notification(SE_QUEUE_ENQUEUE, dolock, NULL, queue, s, pos); } void se_notify_dequeue(int dolock, queue_t *queue, send_t *s) { se_notification(SE_QUEUE_DEQUEUE, dolock, NULL, queue, s, 0); } static void se_pack_notification(se_msg_type_t type, int dolock, packlist_t *list, pack_t *pack) { se_msg_t *m = alloc_msg(); m->type = type; m->pack.list = list; if(pack != NULL) m->pack.pack = pack_clone(pack); else m->pack.pack = NULL; dispatch_msg(m); } void se_notify_pack_add(int dolock, packlist_t *list, pack_t *pack) { se_pack_notification(SE_PACK_ADD, dolock, list, pack); } void se_notify_pack_del(int dolock, packlist_t *list, pack_t *pack) { se_pack_notification(SE_PACK_DEL, dolock, list, pack); } void se_notify_pack_hashed(int dolock, pack_t *pack) { se_pack_notification(SE_PACK_HASHED, dolock, NULL, pack); } void se_notify_packlist_add(packlist_t *list) { se_pack_notification(SE_PACKLIST_ADD, 0, list, NULL); } void se_signal(int no) { se_msg_t *m = alloc_msg(); m->type = SE_SIGNAL; if(no == SIGHUP) m->signal.name = xstrdup("SIGHUP"); else if(no == SIGUSR1) m->signal.name = xstrdup("SIGUSR1"); else if(no == SIGUSR2) m->signal.name = xstrdup("SIGUSR2"); else { char buffer[32]; snprintf(buffer,sizeof(buffer),"SIG%d",no); } dispatch_msg(m); } int se_ui_call(interpreter_t *intp, void *data) { se_msg_t *m = alloc_msg(); m->type = SE_UI_CALL; m->ui.data = data; LOCK(intp); pqueue_push_back(&intp->msgqueue,m); UNLOCK(intp); return 0; } #define DISPATCH(t,struc,ptr) \ do { \ se_msg_t *m = alloc_msg(); \ m->type = t; \ m->struc.struc = ptr; \ dispatch_msg(m); \ } while(0) #define DISPATCH2(t,struc1,ptr1,struc2,ptr2) \ do { \ se_msg_t *m = alloc_msg(); \ m->type = t; \ m->struc1.struc1 = ptr1; \ m->struc1.struc2 = ptr2; \ dispatch_msg(m); \ } while(0) void se_notify_interface_add(interface_t *intf) { DISPATCH2(SE_POOL_ADD,interface,intf,pool,NULL); } void se_notify_pool_add(interface_t *intf, pool_t *p) { DISPATCH2(SE_POOL_ADD,interface,intf,pool,p); } void se_notify_queue_add(queue_t *q) { DISPATCH(SE_QUEUE_ADD,queue,q); } void se_notify_network_add(network_t *net) { DISPATCH2(SE_NETWORK_ADD,network,net,channel,NULL); } void se_notify_channel_add(network_t *net, channel_t *chan) { DISPATCH2(SE_CHANNEL_ADD,network,net,channel,chan); } void se_notify_user_add(user_t *u) { DISPATCH(SE_USER_ADD,user,u); } void se_notify_admin_logon(chat_t *ac, user_t *u) { DISPATCH2(SE_ADMIN_LOGON,admin,ac,user,u); } void se_notify_admin_logoff(user_t *u) { DISPATCH2(SE_ADMIN_LOGOFF,admin,NULL,user,u); } se_save_data_t **se_save_data(int *no_data) { se_save_data_t **data = NULL; interpreter_t **intps = NULL; int i, no_intps = 0; *no_data = 0; LOCKR(&(global->se.lock)); intps = PTRARR_DUP(global->se.interpreters, global->se.no_interpreters,&no_intps); UNLOCKR(&(global->se.lock)); for(i = 0; i < no_intps; ++i) { interpreter_t *intp = intps[i]; LOCK(intp); if(intp->flags & SE_F_LOAD_SAVE) { struct timespec timeout; struct timeval now; se_msg_t *m = alloc_msg(); m->type = SE_SAVE_DATA; LOCK_INIT(&(m->save.lock)); pthread_cond_init(&(m->save.cond),NULL); m->save.data = NULL; m->save.size = 0; LOCKR(&(m->save.lock)); pqueue_push_back(&intp->msgqueue,duplicate_msg(m)); UNLOCK(intp); gettimeofday(&now,NULL); timeout.tv_sec = now.tv_sec + 3; timeout.tv_nsec = now.tv_usec * 1000; if(pthread_cond_timedwait(&(m->save.cond),&(m->save.lock),&timeout) == 0) { if(m->save.data != NULL) { se_save_data_t *d = xalloc(sizeof(se_save_data_t)); LOCK(intp); d->name = xstrdup(intp->name); UNLOCK(intp); d->data = m->save.data; d->size = m->save.size; m->save.data = NULL; PTRARR_ADD(&data,no_data,d); } } UNLOCKR(&(m->save.lock)); free_msg(m); } else UNLOCK(intp); } if(intps != NULL) xfree(intps); return data; } int se_load_data(const char *name, uint8_t *data, size_t size) { int ret = -1; if(global == NULL) { se_save_data_t *d = xalloc(sizeof(se_save_data_t)); d->name = xstrdup(name); d->data = data; d->size = size; PTRARR_ADD(&load_cache,&no_load_cache,d); ret = 0; } else { int i; LOCKR(&(global->se.lock)); for(i = 0; i < global->se.no_interpreters; ++i) { interpreter_t *intp = global->se.interpreters[i]; LOCK(intp); if((intp->flags & SE_F_LOAD_SAVE) && strcmp(intp->name,name) == 0) { se_msg_t *m = alloc_msg(); m->type = SE_LOAD_DATA; m->load.data = data; m->load.size = size; pqueue_push_back(&(intp->msgqueue),m); UNLOCK(intp); ret = 0; break; } else { UNLOCK(intp); } } UNLOCKR(&(global->se.lock)); } return ret; }