xref: /plan9/sys/src/9/port/devenv.c (revision 6a0c38e7e48be1382e8d74973bfe1ab438d920a3)
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 int
envstat(Chan * c,uchar * db,int n)88 envstat(Chan *c, uchar *db, int 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,ulong)141 envcreate(Chan *c, char *name, int omode, ulong)
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 	ulong offset = off;
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 	if(offset > e->len)	/* protects against overflow converting vlong to ulong */
252 		n = 0;
253 	else if(offset + n > e->len)
254 		n = e->len - offset;
255 	if(n <= 0)
256 		n = 0;
257 	else
258 		memmove(a, e->value+offset, n);
259 	runlock(eg);
260 	return n;
261 }
262 
263 static long
envwrite(Chan * c,void * a,long n,vlong off)264 envwrite(Chan *c, void *a, long n, vlong off)
265 {
266 	char *s;
267 	ulong len;
268 	Egrp *eg;
269 	Evalue *e;
270 	ulong offset = off;
271 
272 	if(n <= 0)
273 		return 0;
274 	if(offset > Maxenvsize || n > (Maxenvsize - offset))
275 		error(Etoobig);
276 
277 	eg = envgrp(c);
278 	wlock(eg);
279 	e = envlookup(eg, nil, c->qid.path);
280 	if(e == 0) {
281 		wunlock(eg);
282 		error(Enonexist);
283 	}
284 
285 	len = offset+n;
286 	if(len > e->len) {
287 		s = smalloc(len);
288 		if(e->value){
289 			memmove(s, e->value, e->len);
290 			free(e->value);
291 		}
292 		e->value = s;
293 		e->len = len;
294 	}
295 	memmove(e->value+offset, a, n);
296 	e->qid.vers++;
297 	eg->vers++;
298 	wunlock(eg);
299 	return n;
300 }
301 
302 Dev envdevtab = {
303 	'e',
304 	"env",
305 
306 	devreset,
307 	devinit,
308 	devshutdown,
309 	envattach,
310 	envwalk,
311 	envstat,
312 	envopen,
313 	envcreate,
314 	envclose,
315 	envread,
316 	devbread,
317 	envwrite,
318 	devbwrite,
319 	envremove,
320 	devwstat,
321 };
322 
323 void
envcpy(Egrp * to,Egrp * from)324 envcpy(Egrp *to, Egrp *from)
325 {
326 	int i;
327 	Evalue *ne, *e;
328 
329 	rlock(from);
330 	to->ment = (from->nent+31)&~31;
331 	to->ent = smalloc(to->ment*sizeof(to->ent[0]));
332 	for(i=0; i<from->nent; i++){
333 		e = from->ent[i];
334 		ne = smalloc(sizeof(Evalue));
335 		ne->name = smalloc(strlen(e->name)+1);
336 		strcpy(ne->name, e->name);
337 		if(e->value){
338 			ne->value = smalloc(e->len);
339 			memmove(ne->value, e->value, e->len);
340 			ne->len = e->len;
341 		}
342 		ne->qid.path = ++to->path;
343 		to->ent[i] = ne;
344 	}
345 	to->nent = from->nent;
346 	runlock(from);
347 }
348 
349 void
closeegrp(Egrp * eg)350 closeegrp(Egrp *eg)
351 {
352 	int i;
353 	Evalue *e;
354 
355 	if(decref(eg) == 0){
356 		for(i=0; i<eg->nent; i++){
357 			e = eg->ent[i];
358 			free(e->name);
359 			if(e->value)
360 				free(e->value);
361 			free(e);
362 		}
363 		free(eg->ent);
364 		free(eg);
365 	}
366 }
367 
368 static Egrp*
envgrp(Chan * c)369 envgrp(Chan *c)
370 {
371 	if(c->aux == nil)
372 		return up->egrp;
373 	return c->aux;
374 }
375 
376 static int
envwriteable(Chan * c)377 envwriteable(Chan *c)
378 {
379 	return iseve() || c->aux == nil;
380 }
381 
382 /*
383  *  to let the kernel set environment variables
384  */
385 void
ksetenv(char * ename,char * eval,int conf)386 ksetenv(char *ename, char *eval, int conf)
387 {
388 	Chan *c;
389 	char buf[2*KNAMELEN];
390 
391 	snprint(buf, sizeof(buf), "#e%s/%s", conf?"c":"", ename);
392 	c = namec(buf, Acreate, OWRITE, 0600);
393 	devtab[c->type]->write(c, eval, strlen(eval), 0);
394 	cclose(c);
395 }
396 
397 /*
398  * Return a copy of configuration environment as a sequence of strings.
399  * The strings alternate between name and value.  A zero length name string
400  * indicates the end of the list
401  */
402 char *
getconfenv(void)403 getconfenv(void)
404 {
405 	Egrp *eg = &confegrp;
406 	Evalue *e;
407 	char *p, *q;
408 	int i, n;
409 
410 	rlock(eg);
411 	if(waserror()) {
412 		runlock(eg);
413 		nexterror();
414 	}
415 
416 	/* determine size */
417 	n = 0;
418 	for(i=0; i<eg->nent; i++){
419 		e = eg->ent[i];
420 		n += strlen(e->name) + e->len + 2;
421 	}
422 	p = malloc(n + 1);
423 	if(p == nil)
424 		error(Enomem);
425 	q = p;
426 	for(i=0; i<eg->nent; i++){
427 		e = eg->ent[i];
428 		strcpy(q, e->name);
429 		q += strlen(q) + 1;
430 		memmove(q, e->value, e->len);
431 		q[e->len] = 0;
432 		/* move up to the first null */
433 		q += strlen(q) + 1;
434 	}
435 	*q = 0;
436 
437 	poperror();
438 	runlock(eg);
439 	return p;
440 }
441