xref: /plan9-contrib/sys/src/9k/port/devenv.c (revision 406c76facc4b13aa2a55454bf4091aab9f03da22)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 
8 enum
9 {
10 	Maxenvsize = 16300,
11 };
12 
13 static Egrp	*envgrp(Chan *c);
14 static int	envwriteable(Chan *c);
15 
16 static Egrp	confegrp;	/* global environment group containing the kernel configuration */
17 
18 static Evalue*
envlookup(Egrp * eg,char * name,ulong qidpath)19 envlookup(Egrp *eg, char *name, ulong qidpath)
20 {
21 	Evalue *e;
22 	int i;
23 
24 	for(i=0; i<eg->nent; i++){
25 		e = eg->ent[i];
26 		if(e->qid.path == qidpath || (name && e->name[0]==name[0] && strcmp(e->name, name) == 0))
27 			return e;
28 	}
29 	return nil;
30 }
31 
32 static int
envgen(Chan * c,char * name,Dirtab *,int,int s,Dir * dp)33 envgen(Chan *c, char *name, Dirtab*, int, int s, Dir *dp)
34 {
35 	Egrp *eg;
36 	Evalue *e;
37 
38 	if(s == DEVDOTDOT){
39 		devdir(c, c->qid, "#e", 0, eve, DMDIR|0775, dp);
40 		return 1;
41 	}
42 
43 	eg = envgrp(c);
44 	rlock(eg);
45 	e = 0;
46 	if(name)
47 		e = envlookup(eg, name, -1);
48 	else if(s < eg->nent)
49 		e = eg->ent[s];
50 
51 	if(e == 0) {
52 		runlock(eg);
53 		return -1;
54 	}
55 
56 	/* make sure name string continues to exist after we release lock */
57 	kstrcpy(up->genbuf, e->name, sizeof up->genbuf);
58 	devdir(c, e->qid, up->genbuf, e->len, eve, 0666, dp);
59 	runlock(eg);
60 	return 1;
61 }
62 
63 static Chan*
envattach(char * spec)64 envattach(char *spec)
65 {
66 	Chan *c;
67 	Egrp *egrp = nil;
68 
69 	if(spec && *spec) {
70 		if(strcmp(spec, "c") == 0)
71 			egrp = &confegrp;
72 		if(egrp == nil)
73 			error(Ebadarg);
74 	}
75 
76 	c = devattach('e', spec);
77 	c->aux = egrp;
78 	return c;
79 }
80 
81 static Walkqid*
envwalk(Chan * c,Chan * nc,char ** name,int nname)82 envwalk(Chan *c, Chan *nc, char **name, int nname)
83 {
84 	return devwalk(c, nc, name, nname, 0, 0, envgen);
85 }
86 
87 static long
envstat(Chan * c,uchar * db,long n)88 envstat(Chan *c, uchar *db, long n)
89 {
90 	if(c->qid.type & QTDIR)
91 		c->qid.vers = envgrp(c)->vers;
92 	return devstat(c, db, n, 0, 0, envgen);
93 }
94 
95 static Chan*
envopen(Chan * c,int omode)96 envopen(Chan *c, int omode)
97 {
98 	Egrp *eg;
99 	Evalue *e;
100 	int trunc;
101 
102 	eg = envgrp(c);
103 	if(c->qid.type & QTDIR) {
104 		if(omode != OREAD)
105 			error(Eperm);
106 	}
107 	else {
108 		trunc = omode & OTRUNC;
109 		if(omode != OREAD && !envwriteable(c))
110 			error(Eperm);
111 		if(trunc)
112 			wlock(eg);
113 		else
114 			rlock(eg);
115 		e = envlookup(eg, nil, c->qid.path);
116 		if(e == 0) {
117 			if(trunc)
118 				wunlock(eg);
119 			else
120 				runlock(eg);
121 			error(Enonexist);
122 		}
123 		if(trunc && e->value) {
124 			e->qid.vers++;
125 			free(e->value);
126 			e->value = 0;
127 			e->len = 0;
128 		}
129 		if(trunc)
130 			wunlock(eg);
131 		else
132 			runlock(eg);
133 	}
134 	c->mode = openmode(omode);
135 	c->flag |= COPEN;
136 	c->offset = 0;
137 	return c;
138 }
139 
140 static void
envcreate(Chan * c,char * name,int omode,int)141 envcreate(Chan *c, char *name, int omode, int)
142 {
143 	Egrp *eg;
144 	Evalue *e;
145 	Evalue **ent;
146 
147 	if(c->qid.type != QTDIR)
148 		error(Eperm);
149 	if(strlen(name) >= sizeof up->genbuf)
150 		error("name too long");			/* protect envgen */
151 
152 	omode = openmode(omode);
153 	eg = envgrp(c);
154 
155 	wlock(eg);
156 	if(waserror()) {
157 		wunlock(eg);
158 		nexterror();
159 	}
160 
161 	if(envlookup(eg, name, -1))
162 		error(Eexist);
163 
164 	e = smalloc(sizeof(Evalue));
165 	e->name = smalloc(strlen(name)+1);
166 	strcpy(e->name, name);
167 
168 	if(eg->nent == eg->ment){
169 		eg->ment += 32;
170 		ent = smalloc(sizeof(eg->ent[0])*eg->ment);
171 		if(eg->nent)
172 			memmove(ent, eg->ent, sizeof(eg->ent[0])*eg->nent);
173 		free(eg->ent);
174 		eg->ent = ent;
175 	}
176 	e->qid.path = ++eg->path;
177 	e->qid.vers = 0;
178 	eg->vers++;
179 	eg->ent[eg->nent++] = e;
180 	c->qid = e->qid;
181 
182 	wunlock(eg);
183 	poperror();
184 
185 	c->offset = 0;
186 	c->mode = omode;
187 	c->flag |= COPEN;
188 }
189 
190 static void
envremove(Chan * c)191 envremove(Chan *c)
192 {
193 	int i;
194 	Egrp *eg;
195 	Evalue *e;
196 
197 	if(c->qid.type & QTDIR)
198 		error(Eperm);
199 
200 	eg = envgrp(c);
201 	wlock(eg);
202 	e = 0;
203 	for(i=0; i<eg->nent; i++){
204 		if(eg->ent[i]->qid.path == c->qid.path){
205 			e = eg->ent[i];
206 			eg->nent--;
207 			eg->ent[i] = eg->ent[eg->nent];
208 			eg->vers++;
209 			break;
210 		}
211 	}
212 	wunlock(eg);
213 	if(e == 0)
214 		error(Enonexist);
215 	free(e->name);
216 	if(e->value)
217 		free(e->value);
218 	free(e);
219 }
220 
221 static void
envclose(Chan * c)222 envclose(Chan *c)
223 {
224 	/*
225 	 * cclose can't fail, so errors from remove will be ignored.
226 	 * since permissions aren't checked,
227 	 * envremove can't not remove it if its there.
228 	 */
229 	if(c->flag & CRCLOSE)
230 		envremove(c);
231 }
232 
233 static long
envread(Chan * c,void * a,long n,vlong off)234 envread(Chan *c, void *a, long n, vlong off)
235 {
236 	Egrp *eg;
237 	Evalue *e;
238 	long offset;
239 
240 	if(c->qid.type & QTDIR)
241 		return devdirread(c, a, n, 0, 0, envgen);
242 
243 	eg = envgrp(c);
244 	rlock(eg);
245 	e = envlookup(eg, nil, c->qid.path);
246 	if(e == 0) {
247 		runlock(eg);
248 		error(Enonexist);
249 	}
250 
251 	offset = off;
252 	if(offset > e->len)	/* protects against overflow converting vlong to long */
253 		n = 0;
254 	else if(offset + n > e->len)
255 		n = e->len - offset;
256 	if(n <= 0)
257 		n = 0;
258 	else
259 		memmove(a, e->value+offset, n);
260 	runlock(eg);
261 	return n;
262 }
263 
264 static long
envwrite(Chan * c,void * a,long n,vlong off)265 envwrite(Chan *c, void *a, long n, vlong off)
266 {
267 	char *s;
268 	Egrp *eg;
269 	Evalue *e;
270 	long len, offset;
271 
272 	if(n <= 0)
273 		return 0;
274 	offset = off;
275 	if(offset > Maxenvsize || n > (Maxenvsize - offset))
276 		error(Etoobig);
277 
278 	eg = envgrp(c);
279 	wlock(eg);
280 	e = envlookup(eg, nil, c->qid.path);
281 	if(e == 0) {
282 		wunlock(eg);
283 		error(Enonexist);
284 	}
285 
286 	len = offset+n;
287 	if(len > e->len) {
288 		s = smalloc(len);
289 		if(e->value){
290 			memmove(s, e->value, e->len);
291 			free(e->value);
292 		}
293 		e->value = s;
294 		e->len = len;
295 	}
296 	memmove(e->value+offset, a, n);
297 	e->qid.vers++;
298 	eg->vers++;
299 	wunlock(eg);
300 	return n;
301 }
302 
303 Dev envdevtab = {
304 	'e',
305 	"env",
306 
307 	devreset,
308 	devinit,
309 	devshutdown,
310 	envattach,
311 	envwalk,
312 	envstat,
313 	envopen,
314 	envcreate,
315 	envclose,
316 	envread,
317 	devbread,
318 	envwrite,
319 	devbwrite,
320 	envremove,
321 	devwstat,
322 };
323 
324 void
envcpy(Egrp * to,Egrp * from)325 envcpy(Egrp *to, Egrp *from)
326 {
327 	int i;
328 	Evalue *ne, *e;
329 
330 	rlock(from);
331 	to->ment = (from->nent+31)&~31;
332 	to->ent = smalloc(to->ment*sizeof(to->ent[0]));
333 	for(i=0; i<from->nent; i++){
334 		e = from->ent[i];
335 		ne = smalloc(sizeof(Evalue));
336 		ne->name = smalloc(strlen(e->name)+1);
337 		strcpy(ne->name, e->name);
338 		if(e->value){
339 			ne->value = smalloc(e->len);
340 			memmove(ne->value, e->value, e->len);
341 			ne->len = e->len;
342 		}
343 		ne->qid.path = ++to->path;
344 		to->ent[i] = ne;
345 	}
346 	to->nent = from->nent;
347 	runlock(from);
348 }
349 
350 void
closeegrp(Egrp * eg)351 closeegrp(Egrp *eg)
352 {
353 	int i;
354 	Evalue *e;
355 
356 	if(decref(eg) == 0){
357 		for(i=0; i<eg->nent; i++){
358 			e = eg->ent[i];
359 			free(e->name);
360 			if(e->value)
361 				free(e->value);
362 			free(e);
363 		}
364 		free(eg->ent);
365 		free(eg);
366 	}
367 }
368 
369 static Egrp*
envgrp(Chan * c)370 envgrp(Chan *c)
371 {
372 	if(c->aux == nil)
373 		return up->egrp;
374 	return c->aux;
375 }
376 
377 static int
envwriteable(Chan * c)378 envwriteable(Chan *c)
379 {
380 	return iseve() || c->aux == nil;
381 }
382 
383 /*
384  *  to let the kernel set environment variables
385  */
386 void
ksetenv(char * ename,char * eval,int conf)387 ksetenv(char *ename, char *eval, int conf)
388 {
389 	Chan *c;
390 	char buf[2*KNAMELEN];
391 
392 	snprint(buf, sizeof(buf), "#e%s/%s", conf?"c":"", ename);
393 	c = namec(buf, Acreate, OWRITE, 0600);
394 	c->dev->write(c, eval, strlen(eval), 0);
395 	cclose(c);
396 }
397 
398 /*
399  * Return a copy of configuration environment as a sequence of strings.
400  * The strings alternate between name and value.  A zero length name string
401  * indicates the end of the list
402  */
403 char *
getconfenv(void)404 getconfenv(void)
405 {
406 	Egrp *eg = &confegrp;
407 	Evalue *e;
408 	char *p, *q;
409 	int i, n;
410 
411 	rlock(eg);
412 	if(waserror()) {
413 		runlock(eg);
414 		nexterror();
415 	}
416 
417 	/* determine size */
418 	n = 0;
419 	for(i=0; i<eg->nent; i++){
420 		e = eg->ent[i];
421 		n += strlen(e->name) + e->len + 2;
422 	}
423 	p = malloc(n + 1);
424 	if(p == nil)
425 		error(Enomem);
426 	q = p;
427 	for(i=0; i<eg->nent; i++){
428 		e = eg->ent[i];
429 		strcpy(q, e->name);
430 		q += strlen(q) + 1;
431 		memmove(q, e->value, e->len);
432 		q[e->len] = 0;
433 		/* move up to the first null */
434 		q += strlen(q) + 1;
435 	}
436 	*q = 0;
437 
438 	poperror();
439 	runlock(eg);
440 	return p;
441 }
442