Plan 9 from Bell Labs’s /usr/web/sources/contrib/nemo/sys/src/libomero/panel.c

Copyright © 2021 Plan 9 Foundation.
Distributed under the MIT License.
Download the Plan 9 distribution.


#include <u.h>
#include <libc.h>
#include <thread.h>
#include <error.h>
#include <b.h>
#include <omero.h>

/*
 * Error semantics:
 * 1. Replicas are removed on I/O errors.
 * 2. I/O fails when all the replicas failed.
 * 3. Errors are ignored on closes and removes
 *
 * Locking:
 * - glock held while changing list of panels
 * - g->Lock held while changing g->repl
 * - Events keep a ref to a panel. A panel is
 *   removed when the user asks for it. But the data
 *   structure is released only when all pending
 *   events pointing to the panel have been processed.
 */

static Panel*	panels;
static QLock	glock;	// for all panels.

#define ddump	if(omerodebug>1)paneldump
#define dprint	if(omerodebug)fprint

void
paneldump(int fd)
{
	Panel* g;
	Repl* r;

	qlock(&glock);
	for(g = panels; g; g = g->next){
		qlock(g);
		fprint(fd, "%s %ld ref:\n", g->name, g->ref);
		for (r = g->repl; r; r = r->next)
			fprint(fd, "\t%s dfd=%d cfd=%d\n",
				r->path, r->fd[Fdata], r->fd[Fctl]);
		qunlock(g);
	}
	qunlock(&glock);
}

static int
prefix(char* pref, int prefl, char* path)
{
	if (!strncmp(pref, path, prefl))
	if (path[prefl] == '/' || path[prefl] == 0)
		return 1;
	return 0;
}

static Panel*
panelalloc(char* name)
{
	Panel*	g;

	g = emalloc(sizeof(Panel));
	memset(g, 0, sizeof(*g));
	g->next = panels;
	g->name = estrdup(name);
	assert(g->name);
	panels = g;
	return g;
}

/* Used by newpanel and by unpackevent.
 * This adds a new Ref for Panel.
 */
Panel*
findpanel(char* n, int mkit)
{
	Panel* g;

	qlock(&glock);
	for(g = panels; g; g = g->next)
		if (!strcmp(g->name, n))
			break;
	if (g && g->gone){
		qunlock(&glock);
		return nil;
	}
	if (mkit && !g)
		g = panelalloc(n);
	if (g != nil)	
		incref(g);
	qunlock(&glock);
	return g;
}

Repl*
findrepl(Panel* g, char* path, int mkit)
{
	Repl* r;

	qlock(g);
	for (r = g->repl; r; r = r->next)
		if (!strcmp(r->path, path))
			break;
	if (!r && mkit){
		r = emalloc(sizeof(Repl));
		r->path= strdup(path);
		r->fd[0] = r->fd[1] =-1;
		r->next = g->repl;
		g->repl = r;
		g->nrepl++;
	}
	qunlock(g);
	return r;
}

/* Creates a new panel or returns a
 * ref to one created by an event.
 * This means one Ref into Panel.
 */
Panel*
newpanel(char* path, int mkit)
{
	Panel*	g;
	char*	name;

	name = strrchr(path, '/');
	if (!name){
		werrstr("newpanel: path must be absolute");
		return nil;
	}
	name++;
	if (g = findpanel(name, mkit))
		findrepl(g, path, 1);
	return g;
}


Panel*
mkpanel(char* path)
{
	int	fd;
	Panel*	g;

	fd = create(path, OREAD, 0775|DMDIR);
	if (fd < 0)
		return nil;
	close(fd);
	g =  newpanel(path, 1);
	dprint(2, "mkpanel: %s\n", path);
	return g;
}

static void
rmrepl(Repl* r)
{
	char*	path;

	if (r->fd[0] >= 0)
		close(r->fd[0]);
	if (r->fd[1] >= 0)
		close(r->fd[1]);
	r->fd[0] = r->fd[1] = -1;
	path = smprint("%s/ctl", r->path);
	assert(path);
	remove(path);
	free(path);
	path = smprint("%s/data", r->path);
	assert(path);
	remove(path);
	free(path);
	remove(r->path);
}

Panel*
createsubpanel(Panel* g, char* name)
{
	Repl*	r;
	char*	path;
	Panel*	ng;
	ulong	rnd;
	Panel*	rg;
	Repl**	rl;
	char	e[100];

	rnd = truerand()%10000;
	rg = nil;
	qlock(g);
	e[0] = 0;
	for (rl = &g->repl; r = *rl; ){
		path = smprint("%s/%s.%uld", r->path, name, rnd);
		assert(path);
		ng = mkpanel(path);
		dprint(2, "createsubpanel %s %p\n", path, ng);
		free(path);
		if (ng == nil){
			dprint(2, "createsubpanel: rmrepl: %s\n", r->path);
			rerrstr(e, sizeof(e));
			rmrepl(r);
			*rl = r->next;
			free(r->path);
			free(r);
			g->nrepl--;
		} else {
			ng->evc = g->evc;
			if (rg == nil)
				rg = ng;
			rl = &r->next;
		}
	}
	qunlock(g);
	if (rg == nil)
		werrstr("createsubpanel: %s", e);
	return rg;
}

char*
panelpath(Panel* g)
{
	char*	p;

	qlock(g);
	if (g->repl != nil && g->repl->path != nil)
		p = strdup(g->repl->path);
	else
		p = nil;
	qunlock(g);
	return p;
}

static void
panelfree(Panel* g)
{
	Repl*	gr;
	Repl*	nr;

	if (g == nil)
		return;
	free(g->name);
	for(gr = g->repl; gr; gr = nr){
		nr = gr->next;
		assert(gr->fd[Fctl] < 0);
		assert(gr->fd[Fdata] < 0);
		free(gr->path);
		free(gr);
	}
	free(g);
}

static void
unlinkpanel(Panel* g)
{
	Panel**	gl;

	for(gl = &panels; *gl; gl = &(*gl)->next){
		if (*gl == g){
			*gl = g->next;
			break;
		}
	}
}

void
removepanel(Panel* g)
{
	Repl*	r;
	int	wasgone;

	qlock(g);
	wasgone = g->gone;
	g->gone = 1;
	for (r = g->repl; r; r = r->next){
		dprint(2, "removepanel: rmrepl: %s\n", r->path);
		rmrepl(r);
	}
	qunlock(g);

	if (!wasgone && decref(g) > 0)	// must have events pending.
		return;
	qlock(&glock);
	unlinkpanel(g);
	panelfree(g);
	qunlock(&glock);
}

static int
_openpanel(Panel* g, int omode, int what)
{
	Repl*	r;
	Repl**	rl;
	char*	path;
	char	e[100];

	qlock(g);
	seprint(e, e+sizeof(e), "%s: no replicas", g->name);
	if (g->repl == nil)
		dprint(2, "openpanel: %s: no replicas\n", g->name);
	for (rl = &g->repl; r = *rl; ){
		if (r->fd[what] < 0){
			if (what == Fctl)
				path = smprint("%s/ctl", r->path);
			else
				path = smprint("%s/data", r->path);
			r->fd[what] = open(path, omode);
			free(path);
			if (r->fd[what] < 0){
				rerrstr(e, sizeof(e));
				dprint(2, "openpanel: rmrepl: %s: %r\n", r->path);
				rmrepl(r);
				*rl = r->next;
				free(r->path);
				free(r);
				g->nrepl--;
				continue;
			}
		}
		rl = &r->next;
	}
	qunlock(g);
	if (g->repl == nil){
		werrstr("openpanel: %s", e);
		return -1;
	}
	return 1;
}

static void
_closepanel(Panel* g, int what)
{
	Repl*	r;

	qlock(g);
	for (r = g->repl; r; r = r->next){
		if (r->fd[what] >= 0){
			close(r->fd[what]);
			r->fd[what] = -1;
		}
	}
	qunlock(g);
}

static long
_readpanel(Panel* g, int what, void* buf, long len)
{
	Repl*	r;
	Repl**	rl;
	long	rc, nr;
	char	e[100];

	qlock(g);
	rc = -1;
	seprint(e, e+sizeof(e), "%s: not open", g->name);
	for (rl = &g->repl; r = *rl; )
		if (r->fd[what] >= 0){
			nr = read(r->fd[what], buf, len);
			if (nr >= 0){
				rc = nr;
				break;
			}
			rerrstr(e, sizeof(e));
			dprint(2, "_readpanel: rmrepl: %s: %r\n", r->path);
			rmrepl(r);
			*rl = r->next;
			free(r->path);
			free(r);
			g->nrepl--;
		} else
			rl = &r->next;
	qunlock(g);
	if (rc < 0)
		werrstr("_readpanel: %s", e);
	return rc;
}

Dir*
dirstatpanel(Panel* g)
{
	Repl*	r;
	Repl**	rl;
	Dir*	d;
	char	e[100];
	char*	path;

	qlock(g);
	seprint(e, e+sizeof(e), "%s: no replicas", g->name);
	d = nil;
	for (rl = &g->repl; r = *rl; ){
		if (r->fd[Fdata] >= 0)
			d = dirfstat(r->fd[Fdata]);
		else {
			path = smprint("%s/data", r->path);
			d = dirstat(path);
			free(path);
		}
		if (d != nil)
			break;
		rerrstr(e, sizeof(e));
		dprint(2, "dirstatpanel: rmrepl: %s: %r\n", r->path);
		rmrepl(r);
		*rl = r->next;
		free(r->path);
		free(r);
		g->nrepl--;
	}
	qunlock(g);
	if (d == nil)
		werrstr("_readpanel: %s", e);
	return d;
}

void*
readallpanel(Panel* g, long* l)
{
	Dir*	d;
	char*	buf;

	d = dirstatpanel(g);
	if (d == nil){
		*l = -1;
		return nil;
	}
	buf = malloc(d->length+1);
	*l = d->length;
	free(d);
	if (buf == nil){
		*l = -1;
		return nil;
	}
	if (openpanel(g, OREAD) < 0){
		free(buf);
		return nil;
	}
	*l = readpanel(g, buf, *l);
	closepanel(g);
	if (*l < 0){
		free(buf);
		buf = nil;
	}else
		buf[*l] = 0;
	return buf;
}

/* A ctl write error is ignored. Otherwise, a
 * bad ctl would remove the replica thinking that
 * it has been an error accesing the replica files.
 */
static long
_writepanel(Panel* g, int what, void* buf, long len)
{
	Repl*	r;
	Repl**	rl;
	long	wc;
	long	ec;
	char	e[100];

	if (len == 0)
		return 0;
	qlock(g);
	seprint(e, e+sizeof(e), "%s: no replicas", g->name);
	ec = -1;
	for (rl = &g->repl; r = *rl; ){
		if (r->fd[what] >= 0){
			wc = write(r->fd[what], buf, len);
			if (wc != len){
				if (wc < 0)
					rerrstr(e, sizeof(e));
				else
					strcpy(e, "couldn't write all");
				if (what == Fdata){
					rmrepl(r);
					*rl = r->next;
					free(r->path);
					free(r);
					g->nrepl--;
					werrstr("writepanel: %s", e);
					continue;
				}
			} else
				ec = wc;
		}
		rl = &r->next;
	}
	qunlock(g);
	return ec;
}

int
openpanel(Panel* g, int omode)
{
	return _openpanel(g, omode, Fdata);
}

int
openpanelctl(Panel* g)
{
	return _openpanel(g, ORDWR, Fctl);
}

void
closepanel(Panel* g)
{
	_closepanel(g, Fdata);
}

void
closepanelctl(Panel* g)
{
	_closepanel(g, Fctl);
}

long
writepanel(Panel* g, void* buf, long len)
{
	return _writepanel(g, Fdata, buf, len);
}

long
writepanelctl(Panel* g, void* buf, long len)
{
	return _writepanel(g, Fctl, buf, len);
}

long
readpanel(Panel* g, void* buf, long len)
{
	return _readpanel(g, Fdata, buf, len);
}

long
readpanelctl(Panel* g, void* buf, long len)
{
	return _readpanel(g, Fctl, buf, len);
}

vlong
seekpanel(Panel*g, vlong pos, int type)
{
	Repl*	r;
	vlong	rc;
	int	some;

	rc = -1;
	some = 0;
	qlock(g);
	for (r = g->repl; r; r = r->next)
		if (r->fd[Fdata] >= 0){
			some++;
			rc=seek(r->fd[Fdata], pos, type);
		}
	if (!some)
		werrstr("%s: no replicas", g->name);
	qunlock(g);
	return rc;
}

int
panelctl(Panel* g, char* fmt, ...)
{
	char*	ctl;
	va_list	arg;
	long	rc;

	va_start(arg, fmt);
	ctl = vsmprint(fmt, arg);
	va_end(arg);
	rc = writepanelctl(g, ctl, strlen(ctl));
	free(ctl);
	return rc;
}

void
wpanelexcl(Panel* g, char* what, void* buf, long len, Repl* excl)
{
	Repl*	r;
	char*	path;

	qlock(g);
	for(r = g->repl; r; r = r->next){
		if (r != excl){
			path = smprint("%s/%s", r->path, what);
			if (path)
				writef(path, buf, len);
			free(path);
		}
	}
	qunlock(g);
}

void
rpaneldata(Panel* g, Repl* r)
{
	char*	f;
	void*	data;
	long	len;

	if (g == nil || r == nil)
		return;
	qlock(g);
	if (g->repl && g->nrepl >= 2){
		f = smprint("%s/data", r->path);
		data = readf(f, nil, 0, &len);
		free(f);
		if (data != nil){
			qunlock(g);
			wpanelexcl(g, "data", data, len, r);
			free(data);
			return;
		}
	}
	qunlock(g);
}

void
movepanel(char* from, char* to)
{
	Panel*	g;
	Repl*	r;

	qlock(&glock);
	for(g = panels; g; g = g->next){
		qlock(g);
		for (r = g->repl; r; r = r->next)
			if (!strcmp(from, r->path)){
				free(r->path);
				r->path = strdup(to);
				qunlock(g);
				qunlock(&glock);
				return;
			}
		qunlock(g);
	}
	qunlock(&glock);

	/* To debug problems related to races on panel moves
	 */
	fprint(2, "movepanel: no panel: move from %s to %s\npanels:\n", from, to);
	qlock(&glock);
	for(g = panels; g; g = g->next){
		fprint(2, "%s:\n", g->name);
		qlock(g);
		for (r = g->repl; r; r = r->next)
			fprint(2, "\t%s\n", r->path);
		qunlock(g);
	}
	qunlock(&glock);

	/* No source replica found. Create the target anyway.
	 */
	if (g = newpanel(from, 0))
		decref(g);	// we don't keep the ref. It's > 1 also.
}

Bell Labs OSI certified Powered by Plan 9

(Return to Plan 9 Home Page)

Copyright © 2021 Plan 9 Foundation. All Rights Reserved.
Comments to webmaster@9p.io.