예제 #1
0
int
pdict_add_persistent_change_listener(pdict_t *pd, const char *kpat,
    pdl_notify_func_t notify, void *arg)
{
	pdict_persistent_listener_t *pl;
	static int lid = 1;

	if (!(pl = malloc(sizeof (*pl))))
		return 0;
	memset(pl, 0, sizeof (*pl));
	pl->pdpl_l.pdl_notify = notify;
	pl->pdpl_l.pdl_arg = arg;
	if (regcomp(&pl->pdpl_regex, kpat, REG_EXTENDED | REG_NOSUB) != 0) {
		// XXX todo: communicate error context is not libc
		free(pl); pl = NULL;
		pu_log(PUL_WARN, 0, "Failed regcomp in pdict_add_persistent_change_listener.");
		return 0;
	}

	plist_add((void *)(size_t)lid, pl, &pd->pd_persistent_listeners);

	pl->pdpl_new = 1;
	if (!_pdict_walk_int(pd,
	    pdict_ent_add_persistent_change_listener_dcb, pl)) {
		_pdict_walk_int(pd,
		    pdict_ent_remove_persistent_change_listener_dcb, pl);
		plist_remove((void *)(size_t)lid, &pd->pd_persistent_listeners, NULL);
		regfree(&pl->pdpl_regex);
		free(pl); pl = NULL;
		pu_log(PUL_WARN, 0, "Failed _pdict_walk_int in pdict_add_persistent_change_listener.");
		return 0;
	}
	pl->pdpl_new = 0;
	return lid++;
}
예제 #2
0
static void
result(pds_session_t *pdss, const char *cmdtag, const char *fmt, ...)
{
	va_list va;
	char *formatted;
	char *resbuf;
	char errdesc[256];
	int len;
	char terminator = '\n';
	if(pdss->pdss_client_cr_null)
		terminator = '\0';

	va_start(va, fmt);
	len = pvasprintf(&formatted, fmt, va);
	va_end(va);
	if (!formatted) {
		if (!pdss->pdss_should_close)
			pu_log(PUL_WARN, pdss->pdss_id,
				"problem with pvasprintf;"
				" closing session");
		pdss->pdss_should_close = 1;
		return;
	}

	if (cmdtag)
		len = pasprintf(&resbuf, "%s %s%c", cmdtag, formatted, terminator);
	else
		len = pasprintf(&resbuf, "%s%c", formatted, terminator);
	if (!resbuf) {
		free(formatted); formatted = NULL;
		if (!pdss->pdss_should_close)
			pu_log(PUL_WARN, pdss->pdss_id,
				"insufficient memory to compose command result;"
				" closing session");
		pdss->pdss_should_close = 1;
		return;
	}
	free(formatted); formatted = NULL;

#ifndef _MSC_EXTENSIONS
#ifdef DEBUG_PROTOCOL
	if (debug_protocol)
		write(1, resbuf, len);
#endif
#endif
	if (!pdss->pdss_write(pdss->pdss_wfd, resbuf, len +
	    pdss->pdss_client_cr_null, errdesc, sizeof (errdesc)) &&
	    !pdss->pdss_should_close) {
		char *errbuf;
		pasprintf(&errbuf,
		    "I/O error sending result: %s; closing session", errdesc);
		if (errbuf) {
			pu_log(PUL_WARN, pdss->pdss_id, errbuf);
			free(errbuf); errbuf = NULL;
		}
		pdss->pdss_should_close = 1;
	}
	free(resbuf); resbuf = NULL;
	return;
}
예제 #3
0
static void
result(pds_session_t *pdss, const char *cmdtag, const char *fmt, ...)
{
	va_list va;
	char *formatted;
	char *resbuf;
	char errdesc[256];
	int len;
	char terminator = '\n';
	if(pdss->pdss_client_cr_null)
		terminator = '\0';
	
	va_start(va, fmt);
	pvasprintf(&formatted, fmt, va);
	va_end(va);
	if (!formatted) {
		if (!pdss->pdss_should_close)
			pu_log(PUL_WARN, pdss->pdss_id,"problem with pvasprintf; closing session");
		pdss->pdss_should_close = 1;
		return;
	}
	
	if (cmdtag)
		len = pasprintf(&resbuf, "%s %s%c", cmdtag, formatted, terminator);
	else
		len = pasprintf(&resbuf, "%s%c", formatted, terminator);
	if (!resbuf) {
		free(formatted); formatted = NULL;
		if (!pdss->pdss_should_close)
			pu_log(PUL_WARN, pdss->pdss_id,
				   "insufficient memory to compose command result; closing session");
		pdss->pdss_should_close = 1;
		return;
	}
	free(formatted); formatted = NULL;
	
	if (!pdss->pdss_write(pdss->pdss_wfd, resbuf, len +
						  pdss->pdss_client_cr_null, errdesc, sizeof (errdesc)) &&
	    !pdss->pdss_should_close) {
		char *errbuf;
		pasprintf(&errbuf,
				  "connection closed remotely; closing session (%s)", errdesc);
		if (errbuf) {
			pu_log(PUL_INFO, pdss->pdss_id, errbuf);
			free(errbuf); errbuf = NULL;
		}
		pdss->pdss_should_close = 1;
	}
	free(resbuf); resbuf = NULL;
	return;
}
예제 #4
0
static int
remove_persistent_change_listener_cb(const void *k, const void *v, void *arg)
{
	if (!pdict_remove_persistent_change_listener(arg, (int)k))
		pu_log(PUL_WARN, 0, "insufficient memory");
	return 1;
}
예제 #5
0
int
pdict_ent_remove(pdict_t *pd, const char *k, char **ovp)
{
	pdict_ent_t *n;

	pu_log(PUL_VERB, 0, "Removing in key pdict_ent_remove: %s", k);
	
	if (!ptree_remove((void *)&k, &pd->pd_ents, pdecmp, (void *)&n))
	{
		//pu_log(PUL_INFO, 0, "Failed to remove key in pdict_ent_remove: %s", k);
		return 0;
	}
	
	_pdict_ent_notify(n, PDR_ENTRY_REMOVING, n->pde_val);
	
	if (ovp)
		*ovp = (char *)n->pde_val;
	else {
		free((void *)n->pde_val);
	}
	free((void *)n->pde_key);
	pdict_ent_remove_change_listeners(n);
	free(n);
	return 1;
}
예제 #6
0
//What the hell does this function do??
//it's called every time a key matching a listener changes
static void
notify(const char *k, const char *v, pdict_reason_t r, const char *pde_oldval,
	   void *a)
{
	notify_arg_t *na = a;
	id_pde_map_t *oldp;
	id_pde_map_t *ipm;
	id_pde_map_t cm;
	const char *oldVal;
	int res;
	
	na->na_pdss->pdss_pd_lock(na->na_pdss->pdss_pd_lock_arg);
	cm.ipm_id = na->na_listenid;
	cm.ipm_key = k;
	if (ptree_contains((void *)&cm, na->na_pdss->pdss_pending, ipmcmp, (void **)&ipm)) 
	{
		oldVal = ipm->ipm_val;
		ipm->ipm_val = strdup(v);
		free((void *)oldVal);
		ipm->ipm_reason = r;
		na->na_pdss->pdss_pd_unlock(na->na_pdss->pdss_pd_lock_arg);
		return;
	}
	if (!(ipm = malloc(sizeof (*ipm)))) {
		pu_log(PUL_WARN, na->na_pdss->pdss_id, "some notifications dropped due to low-memory condition");
		na->na_pdss->pdss_pd_unlock(na->na_pdss->pdss_pd_lock_arg);
		return;
	}
	ipm->ipm_id = na->na_listenid;
	ipm->ipm_key = strdup(k);
	ipm->ipm_val = strdup(v);
	ipm->ipm_reason = r;
	if ((res = ptree_replace(ipm, &na->na_pdss->pdss_pending, ipmcmp, (void **)&oldp)) != 0 && oldp) {
		pu_log(PUL_WARN, na->na_pdss->pdss_id, "some notifications dropped due to low-memory condition");
		free((void *)oldp->ipm_key); oldp->ipm_key = NULL;
		free((void *)oldp->ipm_val); oldp->ipm_val = NULL;
		free((void *)oldp); oldp = NULL;
	}
	if (!res) {
		free((void *)ipm->ipm_key); ipm->ipm_key = NULL;
		free((void *)ipm->ipm_val); ipm->ipm_val = NULL;
		free(ipm); ipm = NULL;
		pu_log(PUL_WARN, na->na_pdss->pdss_id, "some notifications dropped due to low-memory condition");
	}
	na->na_pdss->pdss_pd_unlock(na->na_pdss->pdss_pd_lock_arg);
}
예제 #7
0
int
pdict_remove_persistent_change_listener(pdict_t *pd, int id)
{
	pdict_persistent_listener_t *pdpl;

	if (!plist_remove((void *)(size_t)id, &pd->pd_persistent_listeners, (void **)&pdpl) || !pdpl)
	{
		pu_log(PUL_WARN, 0, "Failed plist_remove in pdict_remove_persistent_change_listener.");
		return 0;
	}
	if (!_pdict_walk_int(pd, pdict_ent_remove_persistent_change_listener_dcb, pdpl))
	{
		pu_log(PUL_WARN, 0, "Failed _pdict_walk_int in pdict_remove_persistent_change_listener.");
		return 0;
	}
	regfree(&pdpl->pdpl_regex);
	free(pdpl); pdpl = NULL;
	return 1;
}
예제 #8
0
static int
_pdict_ent_add_persistent_change_listeners(pdict_t *pd, pdict_ent_t *pde)
{
	if (!plist_walk(pd->pd_persistent_listeners, pdict_ent_add_persistent_change_listener_cb, pde)) {
		plist_walk(pd->pd_persistent_listeners, pdict_ent_remove_persistent_change_listener_cb, pde);
		pu_log(PUL_WARN, 0, "Failed to add persistent change listener in _pdict_ent_add_persistent_change_listeners.");
		return 0;
	}
	return 1;
}
예제 #9
0
static ptree_walk_res_t
write_pending(const void *node, int level, void *arg, void *pwra)
{
	id_pde_map_t *ipm = (id_pde_map_t *)node;
	pds_session_t *pdss = arg;
	int buflen;
	char errdesc[256];
	char *buf;

	if (!pdss->pdss_write)
		goto freeing;
	buflen = pasprintf(&buf, "%s%s200-%s is pending, key %s "
	    "latest value \"%s\" (%s)\n", pdss->pdss_report_cmdtag ?
	    pdss->pdss_report_cmdtag : "", pdss->pdss_report_cmdtag ?
	    " " : "", ipm->ipm_id, ipm->ipm_key, ipm->ipm_val,
	    pdict_reason_str(ipm->ipm_reason));
	if (buf) {
#ifndef _MSC_EXTENSIONS
#ifdef DEBUG_PROTOCOL
		if (debug_protocol)
			write(1, buf, buflen);
#endif
#endif
		if (!pdss->pdss_write(pdss->pdss_wfd, buf, buflen +
		    pdss->pdss_client_cr_null, errdesc, sizeof (errdesc))) {
#ifdef DEBUG_PROTOCOL
			pu_log(PUL_WARN, pdss->pdss_id,
			    "write error while reporting:");
			pu_log(PUL_WARN, pdss->pdss_id, errdesc);
#endif
		}
		free(buf); buf = NULL;
	} else {
		pu_log(PUL_WARN, pdss->pdss_id,
		    "some notifications dropped due to low-memory condition");
	}
freeing:
	free((void *)ipm->ipm_key); ipm->ipm_key = NULL;
	free((void *)ipm->ipm_val); ipm->ipm_val = NULL;
	free(ipm); ipm = NULL;

	return 1;
}
예제 #10
0
static int
_pdict_ent_add_change_listener(pdict_ent_t *pde, pdl_notify_func_t notify,
    void *arg)
{
	pdict_listener_t *l;

	if (!(l = malloc(sizeof (*l))))
		return 0;
	memset(l, 0, sizeof (*l));
	l->pdl_notify = notify;
	l->pdl_arg = arg;
	if (!plist_add(l, 0, &pde->pde_listeners)) {
		free(l); l = NULL;
		pu_log(PUL_WARN, 0, "Failed plist_add in _pdict_ent_add_change_listener.");
		return 0;
	}

	return 1;
}
예제 #11
0
static int
_pdict_ent_add_persistent_change_listener(pdict_ent_t *pde,
    pdict_persistent_listener_t *pdpl)
{
	int res;

	if ((res = regexec(&pdpl->pdpl_regex, pde->pde_key, 0, NULL, 0)) != 0)
	{
		return res == REG_NOMATCH;
	}
	if (!_pdict_ent_add_change_listener(pde, pdpl->pdpl_l.pdl_notify, pdpl->pdpl_l.pdl_arg))
	{
		pu_log(PUL_WARN, 0, "Failed to add persistent change listener in _pdict_ent_add_persistent_change_listener.");
		return 0;
	}
	if (pdpl->pdpl_new)
		pdpl->pdpl_l.pdl_notify(pde->pde_key, pde->pde_val, PDR_CURRENT_VALUE, NULL, pdpl->pdpl_l.pdl_arg);
	return 1;
}
예제 #12
0
static ptree_walk_res_t
write_pending(const void *node, int level, void *a, void *pwra)
{
	//void **arg = a;
	id_pde_map_t *ipm = (id_pde_map_t *)node;
	pds_session_t *pdss = (pds_session_t *)a;
	//ptree_node_t **pending = arg[1];
	int buflen;
	char errdesc[256];
	char *buf;
	void *ov;
	
	if (!pdss->pdss_write)
		goto freeing;
	buflen = pasprintf(&buf, "%s%s200-%s is pending, key %s "
					   "latest value \"%s\" (%s)\n", pdss->pdss_report_cmdtag ?
					   pdss->pdss_report_cmdtag : "", pdss->pdss_report_cmdtag ?
					   " " : "", ipm->ipm_id, ipm->ipm_key, ipm->ipm_val,
					   pdict_reason_str(ipm->ipm_reason));
	if (buf) {
		if (!pdss->pdss_write(pdss->pdss_wfd, buf, buflen +
							  pdss->pdss_client_cr_null, errdesc, sizeof (errdesc))) {
			//pu_log(PUL_WARN, pdss->pdss_id, "write error while reporting: %s",errdesc);
		}
		free(buf); buf = NULL;
	} else {
		pu_log(PUL_WARN, pdss->pdss_id, "some notifications dropped due to low-memory condition");
	}
freeing:
	ptree_inorder_walk_remove(&pdss->pdss_pending, &ov, pwra, ipmcmp);
	assert(node == ov);
	free((void *)ipm->ipm_key); //ipm->ipm_key = NULL;
	free((void *)ipm->ipm_val); //ipm->ipm_val = NULL;
	free(ov); ov = NULL;
	
	return 1;
}
예제 #13
0
/*
 * This is started by accept_cb as a thread on a new connection.
 * Calls pd_getline and sends the data it gets to pds_process_line
 * until pd_getline > 0 and !pdss->pdss_should_close
 */
int
pds_session_serve(const pds_session_t *arg)
{
	pds_session_t *pdss = (pds_session_t *)arg;
	char *line = NULL;
	int res;
	
#ifdef _MACOSX
	signal (SIGPIPE, SIG_IGN);
#endif
	
	DPRINT(PUL_INFO, "Started new pds_session_serve thread");
	
	pdss->pdss_errdesc[0] = 0;
	
	/* first thing - Authenticate */
	if(pdss->pdss_auth)
	{
		if(pdss->pdss_auth((pds_session_t *)pdss)){
			pu_log(PUL_WARN, pdss->pdss_id, "Authentication failed or bad version - closing connection");
			goto authfailed;
		}
	}
	
	while ((res = pd_getline(pdss->pdss_readbuf, sizeof (pdss->pdss_readbuf),
							 &pdss->pdss_bufcur, &pdss->pdss_buflen, pdss->pdss_read,
							 pdss->pdss_close, pdss->pdss_rfd, &line, pdss->pdss_errdesc,
							 sizeof (pdss->pdss_errdesc))) > 0 && !pdss->pdss_should_close)
	{
		pds_process_line((pds_session_t *)pdss, line);
		free(line); line=NULL;
	}
	
authfailed:
	
	free(line); line=NULL;
	
	//Shut down the report thread
	pdss->pdss_should_close = 1;

	pthread_mutex_lock(&pdss->pdss_lock);
	if (pdss->pdss_report_thread)
	{
		void *status;
		//pthread_cond_wait(&pdss->pdss_report_cv, &pdss->pdss_lock);
		pthread_mutex_unlock(&pdss->pdss_lock);
		pthread_join(pdss->pdss_report_thread, &status);
		pthread_mutex_lock(&pdss->pdss_lock);
	}
	
	/* remove keys set to expire at end of session */
	pdss->pdss_pd_lock(pdss->pdss_pd_lock_arg);
	plist_walk(pdss->pdss_expire, expired_key_cb, pdss->pdss_pd);
	plist_clear(&pdss->pdss_expire);
	
	/* write_pending will free pending notifications - it will NOT send them because we freed pdss_write */
	pdss->pdss_write = NULL;

	ptree_walk(pdss->pdss_pending, PTREE_POSTORDER, write_pending, ipmcmp, pdss);
	assert(!pdss->pdss_pending);
	//ptree_clear(&pdss->pdss_pending);
	//pdss->pdss_pending = NULL;
	
	//clear the listener list - need to lock the dictionary, 
	//because an add/remove could be enumerating the list as we free it otherwise.
	ptree_walk(pdss->pdss_notify_args, PTREE_POSTORDER, free_na_cb, nacmp, pdss);
	//ptree_clear(&pdss->pdss_notify_args);
	assert(!pdss->pdss_notify_args);
	
	//Close the socket
	pdss->pdss_close(pdss->pdss_wfd, NULL, 0);
	if(pdss->pdss_wfd != pdss->pdss_rfd)
		pdss->pdss_close(pdss->pdss_rfd, NULL, 0);
	pdss->pdss_wfd = pdss->pdss_rfd = INVALID_SOCKET;
	pdss->pdss_close = NULL;
	
	pu_log(PUL_INFO, pdss->pdss_id, "done - session closed");
	
	pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
	pthread_mutex_unlock(&pdss->pdss_lock);
	
	//make sure no cb threads are running
	while(pdss->cb_threads_count)
		SLEEP(10);

	/* now free the pdss!! */
	pds_session_free(pdss);
	
	pu_log(PUL_INFO, 0, "Exiting pds_session_serve thread");
	return 0;
}
예제 #14
0
void
pds_process_line(pds_session_t *pdss, char *line)
{
	regmatch_t pmatch[9];
	char *cmdtag = NULL;
	char *cmdstr = NULL;
	char *key = NULL;
	char *val = NULL;
	char *buf = NULL;
	char *listenid = NULL;
	int reportperiod;
	int forsession;
	int res;
	
	//DPRINT(line);
	
	if ((res = regexec(&setex, line, 7, pmatch, 0)) == 0) {
		getmatchsub(line, &cmdtag, pmatch, 1);
		if (cmdtag && strcmp(cmdtag, "unknown") == 0) {
			result(pdss, cmdtag,
				   "304 unknown is a reserved command tag");
			free(cmdtag); cmdtag = NULL;
			return;
		}
		if (!getmatchsub(line, &key, pmatch, 2) || !key)
			goto fail;
		
		if (!getmatchsub(line, &val, pmatch, 5) &&
		    !getmatchsub(line, &val, pmatch, 4))
			goto fail;
		if (!val)
			goto fail;
		if ((forsession = getmatchsub(line, NULL, pmatch, 6)) > 0) {
			/* mark for expiration, but make sure it's only added once! */
			if(plist_walk(pdss->pdss_expire, is_key_in_list, key))
			{
				char *e;
				if (!(e = strdup(key)))
					goto fail;
				plist_add(e, NULL, &pdss->pdss_expire);
			}
		}
		pdss->pdss_pd_lock(pdss->pdss_pd_lock_arg);
		res = pdict_add(pdss->pdss_pd, key, val, NULL);
		pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
		if (!res)
			result(pdss, cmdtag, "304 set failed");
		else
			result(pdss, cmdtag, "200 set successful");
		if (cmdtag) {
			free(cmdtag); cmdtag = NULL;
		}
		free(key); key = NULL;
		free(val); val = NULL;
		return;
	}
	if ((res = regexec(&listenex, line, 6, pmatch, 0)) == 0) {
		notify_arg_t *nap;
		notify_arg_t na;
		
		getmatchsub(line, &cmdtag, pmatch, 1);
		if (cmdtag && strcmp(cmdtag, "unknown") == 0) {
			result(pdss, cmdtag,
				   "304 unknown is a reserved command tag");
			free(cmdtag); cmdtag = NULL;
			return;
		}
		if ((!getmatchsub(line, &buf, pmatch, 4) && !getmatchsub(line, &buf, pmatch, 3)) || !buf)
			goto fail;
		getmatchsub(line, &listenid, pmatch, 5);
		na.na_pdss = pdss;
		na.na_listenid = listenid;
		pthread_mutex_lock(&pdss->pdss_lock);
		pdss->pdss_pd_lock(pdss->pdss_pd_lock_arg);
		if (!ptree_contains(&na, pdss->pdss_notify_args, nacmp, (void **)&nap)) {
			if (!(nap = malloc(sizeof (*nap))))
			{
				pu_log(PUL_WARN, pdss->pdss_id, "insufficient memory");
				pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
				pthread_mutex_unlock(&pdss->pdss_lock);
				goto fail;
			}
			nap->na_pdss = pdss;
			nap->na_listenid = listenid;
			nap->na_pd_ids = NULL;
			if (!ptree_replace(nap, &pdss->pdss_notify_args, nacmp, NULL)) {
				free(nap); nap = NULL;
				pu_log(PUL_WARN, pdss->pdss_id, "insufficient memory");
				pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
				pthread_mutex_unlock(&pdss->pdss_lock);
				free(listenid); listenid = NULL;
				free(buf); buf = NULL;
				goto fail;
			}
		}
		
		if (!(res = pdict_add_persistent_change_listener(pdss->pdss_pd, buf, notify, (void *)nap))) {
			if (!nap->na_pd_ids) {
				ptree_remove(nap, &pdss->pdss_notify_args, nacmp, NULL);
				free((void *)nap->na_listenid); nap->na_listenid = NULL;
				free(nap); nap = NULL;
			}
			result(pdss, cmdtag, "303 listen not established--bad pattern?");
		} else {
			if (!plist_add((void *)res, NULL, &nap->na_pd_ids)) {
				pdict_remove_persistent_change_listener(pdss->pdss_pd, res);
				if (!nap->na_pd_ids) {
					ptree_remove(nap, &pdss->pdss_notify_args, nacmp, NULL);
					free((void *)nap->na_listenid); nap->na_listenid = NULL;
					free(nap); nap = NULL;
				}
				free(buf); buf = NULL;
				if (cmdtag) {
					free(cmdtag); cmdtag = NULL;
				}
				pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
				pthread_mutex_unlock(&pdss->pdss_lock);
				goto fail;
			} else {
				result(pdss, cmdtag,"200 listening, id %s",
					   listenid);
			}
		}
		pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
		pthread_mutex_unlock(&pdss->pdss_lock);
		free(buf); buf = NULL;
		if (cmdtag) {
			free(cmdtag); cmdtag = NULL;
		}
		return;
	}
	if ((res = regexec(&reportex, line, 5, pmatch, 0)) == 0) {
		getmatchsub(line, &cmdtag, pmatch, 1);
		if (cmdtag && strcmp(cmdtag, "unknown") == 0) {
			result(pdss, cmdtag,
				   "304 unknown is a reserved command tag");
			free(cmdtag); cmdtag = NULL;
			return;
		}
		if (!getmatchsub(line, &buf, pmatch, 2) || !buf)
			goto fail;
		if ((reportperiod = atoi(buf)) < 0 || reportperiod > 10000) {
			result(pdss, cmdtag, "301 invalid report/wait period"
				   "--specify milliseconds or 0 for off (max 10s)");
			if (cmdtag) {
				free(cmdtag); cmdtag = NULL;
			}
			free(buf); buf = NULL;
			return;
		}
		free(buf); buf = NULL;
		getmatchsub(line, &buf, pmatch, 3);
		set_report_period(pdss, cmdtag, buf, reportperiod);
		if (cmdtag) {
			free(cmdtag); cmdtag = NULL;
		}
		if (buf) {
			free(buf); buf = NULL;
		}
		return;
	}
	if ((res = regexec(&waitex, line, 5, pmatch, 0)) == 0) {
		getmatchsub(line, &cmdtag, pmatch, 1);
		if (cmdtag && strcmp(cmdtag, "unknown") == 0) {
			result(pdss, cmdtag,
				   "304 unknown is a reserved command tag");
			free(cmdtag); cmdtag = NULL;
			return;
		}
		if (!getmatchsub(line, &buf, pmatch, 2) || !buf)
			goto fail;
		if (atoi(buf) < 0 || atoi(buf) > 10000) {
			result(pdss, cmdtag, "301 invalid wait period"
				   "--specify milliseconds (max 10s)");
			if (cmdtag) {
				free(cmdtag); cmdtag = NULL;
			}
			return;
		}
		usleep(atoi(buf) * 1000);
		result(pdss, cmdtag, "200 nothin' doin'");
		if (cmdtag) {
			free(cmdtag); cmdtag = NULL;
		}
		return;
	}
	if ((res = regexec(&flushex, line, 5, pmatch, 0)) == 0) {
		getmatchsub(line, &cmdtag, pmatch, 1);
		if (cmdtag && strcmp(cmdtag, "unknown") == 0) {
			result(pdss, cmdtag,
				   "304 unknown is a reserved command tag");
			free(cmdtag); cmdtag = NULL;
			return;
		}
		pthread_mutex_lock(&pdss->pdss_lock);
		pdss->pdss_pd_lock(pdss->pdss_pd_lock_arg);
		_flush(pdss);
		pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
		pthread_mutex_unlock(&pdss->pdss_lock);
		result(pdss, cmdtag, "200 glug glug");
		if (cmdtag) {
			free(cmdtag); cmdtag = NULL;
		}
		return;
	}
	if ((res = regexec(&walkex, line, 5, pmatch, 0)) == 0) {
		wa_t wa;
		
		getmatchsub(line, &cmdtag, pmatch, 1);
		if (cmdtag && strcmp(cmdtag, "unknown") == 0) {
			result(pdss, cmdtag,
				   "304 unknown is a reserved command tag");
			free(cmdtag); cmdtag = NULL;
			return;
		}
		if (!getmatchsub(line, &cmdstr, pmatch, 2) || !cmdstr)
			goto fail;
		if (!getmatchsub(line, &buf, pmatch, 3) || !buf) {
			free(cmdstr); cmdstr = NULL;
			goto fail;
		}
		if ((regcomp(&wa.wa_regex, buf, REG_EXTENDED)) != 0) {
			free(cmdstr); cmdstr = NULL;
			free(buf); buf = NULL;
			result(pdss, cmdtag, "305 expression error"); 
			if (cmdtag) {
				free(cmdtag); cmdtag = NULL;
			}
			return;
		}
		wa.wa_pdss = pdss;
		wa.wa_cmdtag = cmdtag;
		wa.wa_l = NULL;
		pdss->pdss_pd_lock(pdss->pdss_pd_lock_arg);
		if (!pdict_walk(pdss->pdss_pd, add_to_wa_list, &wa)) {
			int e = errno;
			pu_log(PUL_WARN, pdss->pdss_id, "temporary failure: %s", strerror(e));
			result(pdss, cmdtag, "300 temporary failure: %s",
				   strerror(e)); 
		} else {
			if (strcmp(cmdstr, "remove") == 0)
				plist_walk(wa.wa_l, remove_wa_list, &wa);
			else
				plist_walk(wa.wa_l, print_wa_list, &wa);
			result(pdss, cmdtag, "200 done");
		}
		plist_clear(&wa.wa_l);
		pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
		regfree(&wa.wa_regex);
		if (cmdtag) {
			free(cmdtag); cmdtag = NULL;
		}
		free(cmdstr); cmdstr = NULL;
		free(buf); buf = NULL;
		return;
	}
	if ((res = regexec(&ignoreex, line, 5, pmatch, 0)) == 0) {
		notify_arg_t *nap;
		notify_arg_t na;
		void *arg[2];
		int n;
		
		getmatchsub(line, &cmdtag, pmatch, 1);
		if (cmdtag && strcmp(cmdtag, "unknown") == 0) {
			result(pdss, cmdtag,
				   "304 unknown is a reserved command tag");
			if (cmdtag) {
				free(cmdtag); cmdtag = NULL;
			}
			return;
		}
		if (!getmatchsub(line, &listenid, pmatch, 2) || !listenid)
			goto fail;
		pthread_mutex_lock(&pdss->pdss_lock);
		pdss->pdss_pd_lock(pdss->pdss_pd_lock_arg);
		na.na_pdss = pdss;
		na.na_listenid = listenid;
		if (!ptree_remove(&na, &pdss->pdss_notify_args, nacmp,
						  (void **)&nap)) {
			pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
			pthread_mutex_unlock(&pdss->pdss_lock);
			result(pdss, cmdtag, "306 nonexistent key/id");
			if (cmdtag) {
				free(cmdtag); cmdtag = NULL;
			}
			return;
		}
		n = 0;
		plist_walk(nap->na_pd_ids, _count, &n);
		plist_walk(nap->na_pd_ids, remove_persistent_change_listener_cb,
				   pdss->pdss_pd);
		arg[0] = (void *)nap->na_listenid;
		arg[1] = &pdss->pdss_pending;
		ptree_walk(pdss->pdss_pending, PTREE_POSTORDER, remove_pending_id, ipmcmp, arg);
		free((void *)nap->na_listenid); nap->na_listenid = NULL;
		plist_clear(&nap->na_pd_ids);
		free(nap); nap = NULL;
		assert(!ptree_contains(&na, pdss->pdss_notify_args, nacmp, NULL));
		pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
		pthread_mutex_unlock(&pdss->pdss_lock);
		
		result(pdss, cmdtag, "200 %d listener%s ignored", n, n > 1 ? "s"
			   : "");
		if (cmdtag) {
			free(cmdtag); cmdtag = NULL;
		}
		free(listenid);
		return;
	}
	if ((res = regexec(&quitex, line, 5, pmatch, 0)) == 0) {
		getmatchsub(line, &cmdtag, pmatch, 1);
		result(pdss, cmdtag, "200 goodbye");
		pdss->pdss_should_close = 1;
		pdss->pdss_close(pdss->pdss_wfd, NULL, 0);
		if (cmdtag) {
			free(cmdtag); cmdtag = NULL;
		}
		return;
	}
	if ((res = regexec(&getidex, line, 5, pmatch, 0)) == 0) {
		getmatchsub(line, &cmdtag, pmatch, 1);
		result(pdss, cmdtag, "200 %d", pdss->pdss_id);
		if (cmdtag) {
			free(cmdtag); cmdtag = NULL;
		}
		return;
	}
	if ((res = regexec(&okex, line, 5, pmatch, 0)) == 0) {
		if (cmdtag) {
			free(cmdtag); cmdtag = NULL;
		}
		return;
	}
	
	result(pdss, NULL, "400 input unrecognized: %s", line);
	return;
fail:
	result(pdss, cmdtag, "300 command failed: %s", strerror(errno));
	if (cmdtag)
		free (cmdtag);
}
예제 #15
0
static void *
report(void *arg)
{
	pds_session_t *pdss = arg;
	char *cmdtag = NULL;
	int period = 0;
	
	pthread_mutex_lock(&pdss->pdss_lock);
	do {
		/* XXX not periodic */
		/* XXX if expires with nothing pending, wait on condvar */
		period = pdss->pdss_report_period;
		if (period) {
			pthread_mutex_unlock(&pdss->pdss_lock);
			usleep(period * 1000);
			pthread_mutex_lock(&pdss->pdss_lock);
			period = pdss->pdss_report_period;
		}
		if ((cmdtag && pdss->pdss_report_cmdtag && strcmp(cmdtag,
														  pdss->pdss_report_cmdtag) != 0) || (!cmdtag &&
																							  pdss->pdss_report_cmdtag)) {
			if (cmdtag && strcmp(cmdtag, "unknown") != 0)
			{
				free(cmdtag); cmdtag = NULL;
			}
			cmdtag = strdup(pdss->pdss_report_cmdtag);
			if (!cmdtag)
				cmdtag = "unknown";
		} else if (cmdtag && !pdss->pdss_report_cmdtag) {
			if (strcmp(cmdtag, "unknown") != 0)
				free(cmdtag);
			cmdtag = NULL;
		}
		if (!period)
			break;
		pdss->pdss_pd_lock(pdss->pdss_pd_lock_arg);
		if (!pdss->pdss_pending) {
			pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
			continue;
		}
		result(pdss, cmdtag, "200-periodic report follows:");
		pthread_mutex_unlock(&pdss->pdss_lock);
		ptree_walk(pdss->pdss_pending, PTREE_POSTORDER, write_pending, ipmcmp, pdss);
		assert(!pdss->pdss_pending);
		pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);
		result(pdss, cmdtag, "200-that's all for now");
		pthread_mutex_lock(&pdss->pdss_lock);
		period = pdss->pdss_report_period;
		pthread_cond_signal(&pdss->pdss_report_cv);
		pdss->pdss_nreport++;
	} while (period && !pdss->pdss_should_close);
	if (cmdtag && strcmp(cmdtag, "unknown") != 0)
		free(cmdtag);
	cmdtag = NULL;
	if (pdss->pdss_report_cmdtag) {
		free(pdss->pdss_report_cmdtag); pdss->pdss_report_cmdtag = NULL;
	}
	pthread_mutex_unlock(&pdss->pdss_lock);
	pthread_cond_signal(&pdss->pdss_report_cv);
	
	//Don't just set this to 0! we won't be able to join it.
	//pdss->pdss_report_thread = 0;
	
	pu_log(PUL_INFO, 0, "Exiting report thread");
	return 0;
}
예제 #16
0
/*
 * This is started by accept_cb as a thread on a new connection.
 * Calls pd_getline and sends the data it gets to pds_process_line
 * until pd_getline > 0 and !pdss->pdss_should_close
 */
int
pds_session_serve(const pds_session_t *arg)
{
	pds_session_t *pdss = (pds_session_t *)arg;
	char *line;
	int res;
	
#ifdef _MACOSX
	signal (SIGPIPE, SIG_IGN);
#endif

	pdss->pdss_errdesc[0] = 0;
	while ((res = pd_getline(pdss->pdss_readbuf, sizeof (pdss->pdss_readbuf),
	    &pdss->pdss_bufcur, &pdss->pdss_buflen, pdss->pdss_read,
	    pdss->pdss_close, pdss->pdss_rfd, &line, pdss->pdss_errdesc,
	    sizeof (pdss->pdss_errdesc))) > 0 && !pdss->pdss_should_close)
	{
		pds_process_line((pds_session_t *)pdss, line);
		free(line); line=NULL;
	}
	
	free(line); line=NULL;
	pdss->pdss_should_close = 1;

	/*printf("Printing out expiring keys:\n");
	plist_walk(pdss->pdss_expire, print_key, NULL);
	printf("DONE\n");*/

	/* remove keys set to expire at end of session */
	pdss->pdss_pd_lock(pdss->pdss_pd_lock_arg);
	plist_walk(pdss->pdss_expire, expired_key_cb, pdss->pdss_pd);
	plist_clear(&pdss->pdss_expire);
	pdss->pdss_pd_unlock(pdss->pdss_pd_lock_arg);

	pthread_mutex_lock(&pdss->pdss_lock);
	if (pdss->pdss_report_thread)
	{
		void *status;
		//pthread_cond_wait(&pdss->pdss_report_cv, &pdss->pdss_lock);
		pthread_mutex_unlock(&pdss->pdss_lock);
		pthread_join(pdss->pdss_report_thread, &status);
		pthread_mutex_lock(&pdss->pdss_lock);
	}
	ptree_walk(pdss->pdss_notify_args, PTREE_INORDER, free_na_cb, NULL);
	ptree_clear(&pdss->pdss_notify_args);
	pthread_mutex_unlock(&pdss->pdss_lock);

	/* write_pending will free pending notifications */
	pdss->pdss_write = NULL;
	ptree_walk(pdss->pdss_pending, PTREE_INORDER, write_pending, pdss);
	ptree_clear(&pdss->pdss_pending);
	
	assert(!pdss->pdss_pending);
	assert(!pdss->pdss_notify_args);
	pu_log(PUL_INFO, pdss->pdss_id, "done - session closed");
	
	/* now free the pdss!! */
	pds_session_free(pdss);

	return res;
}