Thu, 09 May 2013 13:19:51 +0200
improved configuration reloading
--- a/make/install.mk Mon May 06 14:54:40 2013 +0200 +++ b/make/install.mk Thu May 09 13:19:51 2013 +0200 @@ -42,6 +42,7 @@ cp ../templates/config/init.conf $(INSTALL_DIR)/config/init.conf cp ../templates/config/obj.conf $(INSTALL_DIR)/config/obj.conf cp ../templates/config/mime.types $(INSTALL_DIR)/config/mime.types + cp ../templates/config/acl.conf $(INSTALL_DIR)/config/acl.conf sed s:%%WS_HOST%%:$(HOST):g ../templates/config/server.template > $(INSTALL_DIR)/config/server.conf @echo "copy binaries" mv ../work/bin/webservd ../work/bin/webservd.bin @@ -49,6 +50,10 @@ rm ../work/bin/webservd.bin @echo "copy includes" cp ../src/server/public/nsapi.h $(INSTALL_DIR)/include/nsapi.h + cp ../src/server/public/auth.h $(INSTALL_DIR)/include/auth.h + cp ../src/server/public/acl.h $(INSTALL_DIR)/include/acl.h + cp ../src/server/public/vfs.h $(INSTALL_DIR)/include/vfs.h + cp ../src/server/public/webdav.h $(INSTALL_DIR)/include/webdav.h @echo "copy scripts" sed s:%%WS_INSTALL_DIR%%:$(INSTALL_DIR):g ../templates/bin/startserv.template > $(INSTALL_DIR)/bin/startserv chmod +x $(INSTALL_DIR)/bin/startserv
--- a/src/server/config/acl.h Mon May 06 14:54:40 2013 +0200 +++ b/src/server/config/acl.h Thu May 09 13:19:51 2013 +0200 @@ -43,8 +43,7 @@ char *file; UcxList *namedACLs; // ACLConfig list UcxList *uriACLs; // ACLConfig list - UcxList *pathACLs; // ACLConfig list - + UcxList *pathACLs; // ACLConfig list // temp data ACLConfig *cur; } ACLFile;
--- a/src/server/daemon/acldata.c Mon May 06 14:54:40 2013 +0200 +++ b/src/server/daemon/acldata.c Thu May 09 13:19:51 2013 +0200 @@ -27,6 +27,7 @@ */ #include "../config/acl.h" +#include "../util/atomic.h" #include "config.h" #include "acldata.h" @@ -40,6 +41,30 @@ return dat; } +void acl_data_ref(ACLData *acldata) { + if(acldata) { + ws_atomic_inc32(&acldata->ref); + } +} + +void acl_data_unref(ACLData *acldata) { + uint32_t ref = ws_atomic_dec32(&acldata->ref); + if(ref == 0) { + UcxMapIterator i = ucx_map_iterator(acldata->namedACLs); + WSAcl *acl; + UCX_MAP_FOREACH(acl, i) { + free(acl->ace); + free(acl->ece); + if(acl->acl.authprompt) { + free(acl->acl.authprompt); + } + free(acl); + } + ucx_map_free(acldata->namedACLs); + free(acldata); + } +} + ACLList* acl_get(ACLData *acldata, char *name) { ACLList *acl = ucx_map_cstr_get(acldata->namedACLs, name); return acl;
--- a/src/server/daemon/acldata.h Mon May 06 14:54:40 2013 +0200 +++ b/src/server/daemon/acldata.h Thu May 09 13:19:51 2013 +0200 @@ -42,12 +42,12 @@ typedef struct acl_data { UcxMap *namedACLs; - // TODO - uint32_t ref; } ACLData; ACLData* acl_data_new(); +void acl_data_ref(ACLData *acldata); +void acl_data_unref(ACLData *acldata); ACLList* acl_get(ACLData *acldata, char *name);
--- a/src/server/daemon/config.c Mon May 06 14:54:40 2013 +0200 +++ b/src/server/daemon/config.c Thu May 09 13:19:51 2013 +0200 @@ -104,7 +104,7 @@ /* execute init directive */ int ret = d->func->func(d->param, NULL, NULL); - if(ret != REQ_PROCEED || ret != REQ_NOACTION) { + if(ret != REQ_PROCEED && ret != REQ_NOACTION) { log_ereport( LOG_FAILURE, "Error running Init function %s", @@ -251,6 +251,7 @@ uint32_t ref = ws_atomic_dec32(&cfg->ref); if(ref == 0) { // TODO: free configuration + printf("free ServerConfiguration %d\n", cfg); } } @@ -280,15 +281,21 @@ ConfigFile *f = cfgmgr_get_file(file); if(f == NULL) { f = malloc(sizeof(ConfigFile)); + f->data = NULL; f->file = sstrdup(file); f->reload = mime_conf_reload; // load the file content - f->reload(f, cfg); + //f->reload(f, cfg); + if(cfgmgr_reload_file(f, cfg, NULL)) { + free(f->file.ptr); + free(f); + return -1; + } cfgmgr_attach_file(f); } - cfg->mimetypes = f->data; // TODO: ref + cfg->mimetypes = f->data; return 0; } @@ -531,13 +538,19 @@ ConfigFile *f = cfgmgr_get_file(file); if(f == NULL) { f = malloc(sizeof(ConfigFile)); + f->data = NULL; f->file = sstrdup(file); f->reload = object_conf_reload; - f->reload(f, cfg); + //f->reload(f, cfg); + if(cfgmgr_reload_file(f, cfg, NULL)) { + free(f->file.ptr); + free(f); + return -1; + } cfgmgr_attach_file(f); } - vs->objectfile = sstrdup(file); // TODO: pool - vs->objects = (HTTPObjectConfig*)f->data; // TODO: ref + vs->objectfile = sstrdup(file); + vs->objects = (HTTPObjectConfig*)f->data; // load acl config file file.length = base.length + aclfile.length + 1; @@ -548,11 +561,18 @@ ConfigFile *aclf = cfgmgr_get_file(file); if(aclf == NULL) { aclf = malloc(sizeof(ConfigFile)); + aclf->data = NULL; aclf->file = sstrdup(file); aclf->reload = acl_conf_reload; - aclf->reload(aclf, cfg); + //aclf->reload(aclf, cfg); + if(cfgmgr_reload_file(aclf, cfg, NULL)) { + free(aclf->file.ptr); + free(aclf); + return -1; + } cfgmgr_attach_file(aclf); } + vs->acls = aclf->data; // set the access log for the virtual server // TODO: don't use always the default @@ -565,14 +585,30 @@ int object_conf_reload(ConfigFile *file, ServerConfiguration *cfg) { + HTTPObjectConfig *old_conf = file->data; file->data = load_obj_conf(file->file.ptr); - struct stat s; - if(stat(file->file.ptr, &s) != 0) { - perror("object_conf_reload: stat"); - return -1; + if(old_conf) { + object_conf_unref(old_conf); + } + if(file->data) { + return 0; + } else { + return 1; } - file->last_modified = s.st_mtim.tv_sec; - return 0; +} + +void object_conf_ref(HTTPObjectConfig *conf) { + if(conf) { + ws_atomic_inc32(&conf->ref); + } +} + +void object_conf_unref(HTTPObjectConfig *conf) { + uint32_t ref = ws_atomic_dec32(&conf->ref); + if(ref == 0) { + printf("free HTTPObjectConfig %d\n", conf); + pool_destroy(conf->pool); + } } HTTPObjectConfig* load_obj_conf(char *file) { @@ -586,14 +622,15 @@ } /* create object config */ - HTTPObjectConfig *conf = calloc(sizeof(HTTPObjectConfig), 1); - conf->pool = pool_create(); + pool_handle_t *pool = pool_create(); + HTTPObjectConfig *conf = pool_calloc(pool, sizeof(HTTPObjectConfig), 1); + conf->pool = pool; /* convert ObjectConfig to HTTPObjectConfig */ /* add objects */ conf->nobj = ucx_dlist_size(cfg->objects); - conf->objects = calloc(1, sizeof(httpd_object*)); + conf->objects = pool_calloc(pool, 1, sizeof(httpd_object*)); UcxDlist *objlist = cfg->objects; int i = 0; @@ -604,14 +641,14 @@ char *name = NULL; char *ppath = NULL; if(cob->name.length > 0) { - name = sstrdup(cob->name).ptr; + name = sstrdup_pool(pool, cob->name).ptr; } if(cob->ppath.length > 0) { - ppath = sstrdup(cob->ppath).ptr; + ppath = sstrdup_pool(pool, cob->ppath).ptr; } /* create and add object */ - httpd_object *obj = object_new(name); + httpd_object *obj = object_new(pool, name); obj->path = NULL; conf->objects[i] = obj; // TODO: beyond array bounds write @@ -622,9 +659,9 @@ while(dirs != NULL) { ConfigDirective *cfgdir = dirs->data; - directive *d = malloc(sizeof(directive)); + directive *d = pool_malloc(pool, sizeof(directive)); d->cond = NULL; - d->param = pblock_create_pool(conf->pool, 8); + d->param = pblock_create_pool(pool, 8); /* add params */ UcxList *param = cfg_param_list(cfgdir->value, mp); @@ -662,8 +699,12 @@ int mime_conf_reload(ConfigFile *file, ServerConfiguration *cfg) { MimeConfig *mimecfg = load_mime_config(file->file.ptr); + MimeMap *old_conf = file->data; - UcxMap *mimemap = ucx_map_new((mimecfg->ntypes * 3) / 2); + MimeMap *mimemap = malloc(sizeof(MimeMap)); + mimemap->ref = 1; + UcxMap *map = ucx_map_new((mimecfg->ntypes * 3) / 2); + mimemap->map = map; // add ext type pairs UCX_FOREACH(UcxList*, mimecfg->directives, md) { @@ -672,14 +713,39 @@ UCX_FOREACH(UcxList*, d->exts, xl) { sstr_t ext = sstr(xl->data); sstr_t value = sstrdup(d->type); - ucx_map_sstr_put(mimemap, ext, value.ptr); + ucx_map_sstr_put(map, ext, value.ptr); } } file->data = mimemap; + + if(old_conf) { + mime_conf_unref(old_conf); + } + return 0; } +void mime_conf_ref(MimeMap *conf) { + if(conf) { + ws_atomic_inc32(&conf->ref); + } +} + +void mime_conf_unref(MimeMap *conf) { + uint32_t ref = ws_atomic_dec32(&conf->ref); + if(ref == 0) { + printf("free MimeConfig %d\n", conf); + UcxMapIterator i = ucx_map_iterator(conf->map); + char *str; + UCX_MAP_FOREACH(str, i) { + free(str); + } + ucx_map_free(conf->map); + free(conf); + } +} + int acl_conf_reload(ConfigFile *file, ServerConfiguration *cfg) { ACLFile *aclfile = load_acl_file(file->file.ptr); @@ -691,14 +757,12 @@ } free_acl_file(aclfile); - cfg->acls = acldata; + ACLData *old_data = file->data; + file->data = acldata; + if(old_data) { + acl_data_unref(old_data); + } - struct stat s; - if(stat(file->file.ptr, &s) != 0) { - perror("object_conf_reload: stat"); - return -1; - } - file->last_modified = s.st_mtim.tv_sec; return 0; }
--- a/src/server/daemon/config.h Mon May 06 14:54:40 2013 +0200 +++ b/src/server/daemon/config.h Thu May 09 13:19:51 2013 +0200 @@ -51,15 +51,15 @@ extern "C" { #endif - +typedef struct mime_map MimeMap; + typedef struct _server_configuration { pool_handle_t *pool; UcxMap *host_vs; // map of all vservers. key is the host name UcxList *listeners; // list of all listeners UcxList *logfiles; UcxMap *authdbs; - UcxMap *mimetypes; - ACLData *acls; + MimeMap *mimetypes; sstr_t tmp; sstr_t user; uint32_t ref; // reference counter @@ -77,6 +77,10 @@ void *data; }; +struct mime_map { + UcxMap *map; + uint32_t ref; +}; int load_init_conf(char *file); @@ -104,8 +108,12 @@ int object_conf_reload(ConfigFile *file, ServerConfiguration *cfg); +void object_conf_ref(HTTPObjectConfig *conf); +void object_conf_unref(HTTPObjectConfig *conf); HTTPObjectConfig* load_obj_conf(char *file); int mime_conf_reload(ConfigFile *file, ServerConfiguration *cfg); +void mime_conf_ref(MimeMap *conf); +void mime_conf_unref(MimeMap *conf); int acl_conf_reload(ConfigFile *file, ServerConfiguration *cfg); ACLList* acl_config_convert(ServerConfiguration *cfg, ACLConfig *acl);
--- a/src/server/daemon/configmanager.c Mon May 06 14:54:40 2013 +0200 +++ b/src/server/daemon/configmanager.c Thu May 09 13:19:51 2013 +0200 @@ -94,6 +94,32 @@ return vs; } +int cfgmgr_reload_file(ConfigFile *f, ServerConfiguration *conf, int *reload) { + struct stat s; + if(stat(f->file.ptr, &s) != 0) { + fprintf( + stderr, + "Error: Cannot get stat of file %s\n", f->file.ptr); + perror("cfgmgr_load_config: stat"); + return -1; + } + + //printf("1 time: %d - %d\n", f->last_modified, s.st_mtim.tv_sec); + if(f->last_modified != s.st_mtim.tv_sec) { + /* reload the file */ + printf("reload: %s\n", f->file.ptr); + log_ereport( + LOG_INFORM, + "reload configuration file: %s", + f->file.ptr); + f->reload(f, conf); + f->last_modified = s.st_mtim.tv_sec; + if(reload) { + *reload = 1; + } + } + return 0; +} int cfgmgr_load_config() { int cfgreload = 0; @@ -102,25 +128,9 @@ UcxMapIterator iter = ucx_map_iterator(config_files); ConfigFile *f; UCX_MAP_FOREACH(f, iter) { - struct stat s; - if(stat(f->file.ptr, &s) != 0) { - fprintf( - stderr, - "Error: Cannot get stat of file %s\n", f->file.ptr); - perror("cfgmgr_load_config: stat"); + if(cfgmgr_reload_file(f, current_config, &cfgreload) == -1) { return -1; } - - if(f->last_modified != s.st_mtim.tv_sec) { - /* reload the file */ - //printf("reload: %s\n", sstrdup(f->file).ptr); - log_ereport( - LOG_INFORM, - "reload configuration file: %s", - f->file.ptr); - f->reload(f, current_config); - cfgreload = 1; - } } struct stat s; @@ -181,13 +191,20 @@ printf("no reconfig required!\n"); config = current_config; } - + ServerConfiguration *old_conf = NULL; + if(current_config != config) { + old_conf = current_config; + } current_config = config; + if(old_conf) { + cfg_unref(old_conf); + } return 0; } ServerConfiguration *cfgmgr_get_server_config() { + //cfg_ref(current_config); return current_config; }
--- a/src/server/daemon/configmanager.h Mon May 06 14:54:40 2013 +0200 +++ b/src/server/daemon/configmanager.h Thu May 09 13:19:51 2013 +0200 @@ -49,7 +49,7 @@ void cfgmgr_attach_file(ConfigFile *cf); ConfigFile* cfgmgr_get_file(sstr_t name); - +int cfgmgr_reload_file(ConfigFile *f, ServerConfiguration *conf, int *reload); int cfgmgr_load_config(); ServerConfiguration* cfgmgr_get_server_config();
--- a/src/server/daemon/httplistener.c Mon May 06 14:54:40 2013 +0200 +++ b/src/server/daemon/httplistener.c Thu May 09 13:19:51 2013 +0200 @@ -83,7 +83,9 @@ // TODO: error } + newls->name = conf->name; newls->cfg = conf->cfg; + newls->nacceptors = conf->nacceptors; newls->default_vs.vs_name = conf->vs.ptr; newls->port = fl->port; newls->server_socket = fl->server_socket; @@ -273,6 +275,8 @@ conn->address = ca; conn->fd = clientfd; conn->listener = ls; + + cfg_ref(ls->cfg); /* enqueue the connection */ ls->session_handler->enqueue_connection(
--- a/src/server/daemon/httprequest.c Mon May 06 14:54:40 2013 +0200 +++ b/src/server/daemon/httprequest.c Thu May 09 13:19:51 2013 +0200 @@ -400,6 +400,8 @@ // TODO: keep alive close(sn->connection->fd); + cfg_unref(sn->config); + // free all memory free(sn->connection); @@ -407,7 +409,6 @@ free(sn->netbuf); pool_destroy(sn->sn.pool); - return 0; }
--- a/src/server/daemon/request.c Mon May 06 14:54:40 2013 +0200 +++ b/src/server/daemon/request.c Thu May 09 13:19:51 2013 +0200 @@ -103,3 +103,7 @@ return 0; } + +const VirtualServer* request_get_vs(Request *rq) { + return ((NSAPIRequest*)rq)->vs; +}
--- a/src/server/daemon/vserver.c Mon May 06 14:54:40 2013 +0200 +++ b/src/server/daemon/vserver.c Thu May 09 13:19:51 2013 +0200 @@ -32,6 +32,7 @@ VirtualServer *vs = malloc(sizeof(VirtualServer)); vs->objects = NULL; vs->document_root = sstr("docs"); + vs->acls = NULL; vs->log = NULL; vs->ref = 1; return vs; @@ -44,6 +45,8 @@ newvs->host = sstrdup_pool(pool, vs->host); newvs->name = sstrdup_pool(pool, vs->name); newvs->objectfile = sstrdup_pool(pool, vs->objectfile); + newvs->acls = vs->acls; + acl_data_ref(newvs->acls); newvs->log = vs->log; // TODO: ref newvs->objects = vs->objects;
--- a/src/server/daemon/vserver.h Mon May 06 14:54:40 2013 +0200 +++ b/src/server/daemon/vserver.h Thu May 09 13:19:51 2013 +0200 @@ -31,7 +31,7 @@ #include "../util/object.h" #include "../public/nsapi.h" - +#include "../daemon/acldata.h" #include "../ucx/string.h" #include "log.h" @@ -50,6 +50,7 @@ sstr_t document_root; + ACLData *acls; AccessLog *log; uint32_t ref; // reference counter
--- a/src/server/public/nsapi.h Mon May 06 14:54:40 2013 +0200 +++ b/src/server/public/nsapi.h Thu May 09 13:19:51 2013 +0200 @@ -1183,6 +1183,13 @@ NSAPI_PUBLIC conf_global_vars_s* conf_getglobals(); +/* + * Query the VirtualServer* associated with a given Request*. + */ +const VirtualServer* request_get_vs(Request *rq); +#define request_get_vs request_get_vs + + ssize_t net_write(SYS_NETFD fd, void *buf, size_t nbytes); ssize_t net_printf(SYS_NETFD fd, char *format, ...);
--- a/src/server/safs/init.c Mon May 06 14:54:40 2013 +0200 +++ b/src/server/safs/init.c Thu May 09 13:19:51 2013 +0200 @@ -31,6 +31,7 @@ #include "init.h" #include "../ucx/string.h" #include "../daemon/func.h" +#include "../daemon/log.h" int init_test(pblock *pb, Session *sn, Request *rq) { printf("init-test\n");
--- a/src/server/safs/objecttype.c Mon May 06 14:54:40 2013 +0200 +++ b/src/server/safs/objecttype.c Thu May 09 13:19:51 2013 +0200 @@ -60,7 +60,7 @@ /* get the mime type for the ext from the server configuration */ ServerConfiguration *config = session_get_config(sn); - char *type = ucx_map_sstr_get(config->mimetypes, ext); + char *type = ucx_map_sstr_get(config->mimetypes->map, ext); if(!type) { return REQ_NOACTION;
--- a/src/server/safs/pathcheck.c Mon May 06 14:54:40 2013 +0200 +++ b/src/server/safs/pathcheck.c Thu May 09 13:19:51 2013 +0200 @@ -33,6 +33,7 @@ #include "../daemon/acl.h" #include "../daemon/acldata.h" #include "../daemon/session.h" +#include "../daemon/vserver.h" #include "../ucx/string.h" #include "../config/acl.h" @@ -71,11 +72,11 @@ } int append_acl(pblock *pb, Session *sn, Request *rq) { - ServerConfiguration *config = session_get_config(sn); + const VirtualServer *vs = request_get_vs(rq); char *aclname = pblock_findval("acl", pb); if(aclname) { - ACLList *acl = acl_get(config->acls, aclname); + ACLList *acl = acl_get(vs->acls, aclname); if(!acl) { // TODO: error fprintf(stderr, "acl %s not found\n", aclname);
--- a/src/server/util/atomic.h Mon May 06 14:54:40 2013 +0200 +++ b/src/server/util/atomic.h Thu May 09 13:19:51 2013 +0200 @@ -44,7 +44,7 @@ // use atomic.h #include <atomic.h> -#define ws_atomic_inc32(intptr) atomic_dec_32_nv(intptr) +#define ws_atomic_inc32(intptr) atomic_inc_32_nv(intptr) #define ws_atomic_dec32(intptr) atomic_dec_32_nv(intptr) // TODO
--- a/src/server/util/object.c Mon May 06 14:54:40 2013 +0200 +++ b/src/server/util/object.c Thu May 09 13:19:51 2013 +0200 @@ -35,23 +35,23 @@ -httpd_object* object_new(char *name) { +httpd_object* object_new(pool_handle_t *pool, char *name) { // TODO: Speicherverwaltung - httpd_object *obj = malloc(sizeof(httpd_object)); + httpd_object *obj = pool_malloc(pool, sizeof(httpd_object)); + obj->pool = pool; obj->name = name; obj->path = NULL; // create directive table - obj->dt = calloc(sizeof(struct dtable), NUM_NSAPI_TYPES - 1); + obj->dt = pool_calloc(pool, NUM_NSAPI_TYPES - 1, sizeof(struct dtable)); obj->nd = NUM_NSAPI_TYPES - 1; return obj; } void object_free(httpd_object *obj) { - free(obj->name); - // TODO: free objects - free(obj->dt); + //free(obj->name); + //free(obj->dt); } @@ -60,14 +60,14 @@ // allocate space for the new directive //l->dirs = realloc(l->dirs, (l->ndir+1)*sizeof(void*)); - /* TODO: aus irgend einem Grund funktioniert realloc nicht. warum?? */ + // TODO: use realloc - directive **drs = malloc((l->ndir+1)*sizeof(void*)); + directive **drs = pool_malloc(obj->pool, (l->ndir+1)*sizeof(void*)); for(int i=0;i<l->ndir;i++) { drs[i] = l->dirs[i]; } if(l->dirs != NULL) { - free(l->dirs); + pool_free(obj->pool, l->dirs); } l->dirs = drs; @@ -95,31 +95,6 @@ os->pos++; } - - -// TODO: remove -httpd_objset* create_test_objset() { - httpd_objset *objset = malloc(sizeof(httpd_objset)); - objset->obj = calloc(1, sizeof(httpd_object*)); - - httpd_object *obj = object_new("default"); - objset->obj[0] = obj; - objset->pos = 1; - - directive *d1 = malloc(sizeof(directive)); - d1->func = get_function("test-nametrans"); - d1->param = NULL; - object_add_directive(obj, d1, NSAPINameTrans); - - directive *d2 = malloc(sizeof(directive)); - d2->func = get_function("test-service"); - d2->param = NULL; - object_add_directive(obj, d2, NSAPIService); - - return objset; -} - - void httpobjconf_add_object(HTTPObjectConfig *conf, httpd_object *obj) { conf->nobj++; conf->objects = realloc(conf->objects, conf->nobj * sizeof(void*));
--- a/src/server/util/object.h Mon May 06 14:54:40 2013 +0200 +++ b/src/server/util/object.h Thu May 09 13:19:51 2013 +0200 @@ -67,10 +67,11 @@ }; struct httpd_object { - char *name; - char *path; - dtable *dt; - int nd; + pool_handle_t *pool; + char *name; + char *path; + dtable *dt; + int nd; }; struct httpd_objset { @@ -102,12 +103,13 @@ httpd_object **objects; int nobj; pool_handle_t *pool; + uint32_t ref; // reference counter }; /* * creates a new httpd_object */ -httpd_object* object_new(char *name); +httpd_object* object_new(pool_handle_t *pool, char *name); /* * frees an httpd_object
--- a/templates/bin/reconfig.template Mon May 06 14:54:40 2013 +0200 +++ b/templates/bin/reconfig.template Thu May 09 13:19:51 2013 +0200 @@ -2,5 +2,5 @@ PID=`cat /tmp/webserver-rw6pgl8b/pid` -kill -SIGUSR1 $PID +kill -USR1 $PID
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/templates/config/acl.conf Thu May 09 13:19:51 2013 +0200 @@ -0,0 +1,13 @@ +# +# acl.conf +# +# Example: + +# ACL name="name" +# Authenticate authdb="authdb" prompt="Login" +# user:username:read,write,list,delete:allow +# @everyone@:write,delete:deny +# + + +