xref: /inferno-os/emu/Linux/os.c (revision aee7f58dfcd519d45482e45ab1ce6026c846dc21)
1 #include	<sys/types.h>
2 #include	<time.h>
3 #include	<termios.h>
4 #include	<signal.h>
5 #include 	<pwd.h>
6 #include	<sched.h>
7 #include	<sys/resource.h>
8 #include	<sys/wait.h>
9 #include	<sys/time.h>
10 
11 #include	"dat.h"
12 #include	"fns.h"
13 #include	"error.h"
14 #include <fpuctl.h>
15 
16 /* glibc 2.3.3-NTPL messes up getpid() by trying to cache the result, so we'll do it ourselves */
17 #include	<sys/syscall.h>
18 #define	getpid()	syscall(SYS_getpid)
19 
20 /* temporarily suppress CLONE_PTRACE so it works on broken Linux kernels */
21 #undef CLONE_PTRACE
22 #define	CLONE_PTRACE	0
23 
24 enum
25 {
26 	DELETE	= 0x7f,
27 	CTRLC	= 'C'-'@',
28 	NSTACKSPERALLOC = 16,
29 	X11STACK=	256*1024
30 };
31 char *hosttype = "Linux";
32 
33 static void *stackalloc(Proc *p, void **tos);
34 static void stackfreeandexit(void *stack);
35 
36 extern int dflag;
37 
38 int	gidnobody = -1;
39 int	uidnobody = -1;
40 static struct 	termios tinit;
41 
42 void
43 pexit(char *msg, int t)
44 {
45 	Osenv *e;
46 	void *kstack;
47 
48 	lock(&procs.l);
49 	if(up->prev)
50 		up->prev->next = up->next;
51 	else
52 		procs.head = up->next;
53 
54 	if(up->next)
55 		up->next->prev = up->prev;
56 	else
57 		procs.tail = up->prev;
58 	unlock(&procs.l);
59 
60 	if(0)
61 		print("pexit: %s: %s\n", up->text, msg);
62 
63 	e = up->env;
64 	if(e != nil) {
65 		closefgrp(e->fgrp);
66 		closepgrp(e->pgrp);
67 		closeegrp(e->egrp);
68 		closesigs(e->sigs);
69 	}
70 	kstack = up->kstack;
71 	free(up->prog);
72 	free(up);
73 	if(kstack != nil)
74 		stackfreeandexit(kstack);
75 }
76 
77 void
78 tramp(void *arg)
79 {
80 	Proc *p;
81 	p = arg;
82 	p->pid = p->sigid = getpid();
83 	(*p->func)(p->arg);
84 	pexit("{Tramp}", 0);
85 }
86 
87 int
88 kproc(char *name, void (*func)(void*), void *arg, int flags)
89 {
90 	int pid;
91 	Proc *p;
92 	Pgrp *pg;
93 	Fgrp *fg;
94 	Egrp *eg;
95 	void *tos;
96 
97 	p = newproc();
98 	if(0)
99 		print("start %s:%.8lx\n", name, p);
100 	if(p == nil) {
101 		print("kproc(%s): no memory", name);
102 		return;
103 	}
104 
105 	if(flags & KPDUPPG) {
106 		pg = up->env->pgrp;
107 		incref(&pg->r);
108 		p->env->pgrp = pg;
109 	}
110 	if(flags & KPDUPFDG) {
111 		fg = up->env->fgrp;
112 		incref(&fg->r);
113 		p->env->fgrp = fg;
114 	}
115 	if(flags & KPDUPENVG) {
116 		eg = up->env->egrp;
117 		incref(&eg->r);
118 		p->env->egrp = eg;
119 	}
120 
121 	p->env->uid = up->env->uid;
122 	p->env->gid = up->env->gid;
123 	kstrdup(&p->env->user, up->env->user);
124 
125 	strcpy(p->text, name);
126 
127 	p->func = func;
128 	p->arg = arg;
129 
130 	if(flags & KPX11){
131 		p->kstack = nil;	/* never freed; also up not defined */
132 		tos = (char*)mallocz(X11STACK, 0) + X11STACK - sizeof(vlong);
133 	}else
134 		p->kstack = stackalloc(p, &tos);
135 
136 	lock(&procs.l);
137 	if(procs.tail != nil) {
138 		p->prev = procs.tail;
139 		procs.tail->next = p;
140 	}
141 	else {
142 		procs.head = p;
143 		p->prev = nil;
144 	}
145 	procs.tail = p;
146 	unlock(&procs.l);
147 
148 	if (__clone(tramp, tos, CLONE_PTRACE|CLONE_VM|CLONE_FS|CLONE_FILES|SIGCHLD, p) <= 0)
149 		panic("kproc: clone failed");
150 
151 	return 0;
152 }
153 
154 /*
155  * TO DO:
156  * To get pc on trap, use sigaction instead of signal and
157  * examine its siginfo structure
158  */
159 
160 /*
161 static void
162 diserr(char *s, int pc)
163 {
164 	char buf[ERRMAX];
165 
166 	snprint(buf, sizeof(buf), "%s: pc=0x%lux", s, pc);
167 	disfault(nil, buf);
168 }
169 */
170 
171 static void
172 trapILL(int signo)
173 {
174 	USED(signo);
175 	disfault(nil, "Illegal instruction");
176 }
177 
178 static void
179 trapBUS(int signo)
180 {
181 	USED(signo);
182 	disfault(nil, "Bus error");
183 }
184 
185 static void
186 trapSEGV(int signo)
187 {
188 	USED(signo);
189 	disfault(nil, "Segmentation violation");
190 }
191 
192 static void
193 trapFPE(int signo)
194 {
195 	char buf[64];
196 	USED(signo);
197 	snprint(buf, sizeof(buf), "sys: fp: exception status=%.4lux", getfsr());
198 	disfault(nil, buf);
199 }
200 
201 static void
202 trapUSR1(int signo)
203 {
204 	int intwait;
205 
206 	USED(signo);
207 
208 	intwait = up->intwait;
209 	up->intwait = 0;	/* clear it to let proc continue in osleave */
210 
211 	if(up->type != Interp)		/* Used to unblock pending I/O */
212 		return;
213 
214 	if(intwait == 0)		/* Not posted so it's a sync error */
215 		disfault(nil, Eintr);	/* Should never happen */
216 }
217 
218 /* called to wake up kproc blocked on a syscall */
219 void
220 oshostintr(Proc *p)
221 {
222 	kill(p->sigid, SIGUSR1);
223 }
224 
225 static void
226 trapUSR2(int signo)
227 {
228 	USED(signo);
229 	/* we've done our work of interrupting sigsuspend */
230 }
231 
232 void
233 osblock(void)
234 {
235 	sigset_t mask;
236 
237 	sigprocmask(SIG_SETMASK, NULL, &mask);
238 	sigdelset(&mask, SIGUSR2);
239 	sigsuspend(&mask);
240 }
241 
242 void
243 osready(Proc *p)
244 {
245 	if(kill(p->sigid, SIGUSR2) < 0)
246 		fprint(2, "emu: osready failed: pid %d: %s\n", p->sigid, strerror(errno));
247 }
248 
249 void
250 oslongjmp(void *regs, osjmpbuf env, int val)
251 {
252 	USED(regs);
253 	siglongjmp(env, val);
254 }
255 
256 static void
257 termset(void)
258 {
259 	struct termios t;
260 
261 	tcgetattr(0, &t);
262 	tinit = t;
263 	t.c_lflag &= ~(ICANON|ECHO|ISIG);
264 	t.c_cc[VMIN] = 1;
265 	t.c_cc[VTIME] = 0;
266 	tcsetattr(0, TCSANOW, &t);
267 }
268 
269 static void
270 termrestore(void)
271 {
272 	tcsetattr(0, TCSANOW, &tinit);
273 }
274 
275 void
276 cleanexit(int x)
277 {
278 	USED(x);
279 
280 	if(up->intwait) {
281 		up->intwait = 0;
282 		return;
283 	}
284 
285 	if(dflag == 0)
286 		termrestore();
287 
288 	kill(0, SIGKILL);
289 	exit(0);
290 }
291 
292 void
293 osreboot(char *file, char **argv)
294 {
295 	if(dflag == 0)
296 		termrestore();
297 	execvp(file, argv);
298 	error("reboot failure");
299 }
300 
301 void
302 libinit(char *imod)
303 {
304 	struct termios t;
305 	struct sigaction act;
306 	sigset_t mask;
307 	struct passwd *pw;
308 	Proc *p;
309 	void *tos;
310 	char sys[64];
311 
312 	setsid();
313 
314 	gethostname(sys, sizeof(sys));
315 	kstrdup(&ossysname, sys);
316 	pw = getpwnam("nobody");
317 	if(pw != nil) {
318 		uidnobody = pw->pw_uid;
319 		gidnobody = pw->pw_gid;
320 	}
321 
322 	if(dflag == 0)
323 		termset();
324 
325 	memset(&act, 0 , sizeof(act));
326 	act.sa_handler = trapUSR1;
327 	sigaction(SIGUSR1, &act, nil);
328 
329 	sigemptyset(&mask);
330 	sigaddset(&mask, SIGUSR2);
331 	sigprocmask(SIG_BLOCK, &mask, NULL);
332 
333 	memset(&act, 0 , sizeof(act));
334 	act.sa_handler = trapUSR2;
335 	sigaction(SIGUSR2, &act, nil);
336 
337 	act.sa_handler = SIG_IGN;
338 	sigaction(SIGCHLD, &act, nil);
339 
340 	/*
341 	 * For the correct functioning of devcmd in the
342 	 * face of exiting slaves
343 	 */
344 	signal(SIGPIPE, SIG_IGN);
345 	if(signal(SIGTERM, SIG_IGN) != SIG_IGN)
346 		signal(SIGTERM, cleanexit);
347 	if(signal(SIGINT, SIG_IGN) != SIG_IGN)
348 		signal(SIGINT, cleanexit);
349 
350 	if(sflag == 0) {
351 		act.sa_handler = trapBUS;
352 		sigaction(SIGBUS, &act, nil);
353 		act.sa_handler = trapILL;
354 		sigaction(SIGILL, &act, nil);
355 		act.sa_handler = trapSEGV;
356 		sigaction(SIGSEGV, &act, nil);
357 		act.sa_handler = trapFPE;
358 		sigaction(SIGFPE, &act, nil);
359 	}
360 
361 	p = newproc();
362 	p->kstack = stackalloc(p, &tos);
363 
364 	pw = getpwuid(getuid());
365 	if(pw != nil)
366 		kstrdup(&eve, pw->pw_name);
367 	else
368 		print("cannot getpwuid\n");
369 
370 	p->env->uid = getuid();
371 	p->env->gid = getgid();
372 
373 	executeonnewstack(tos, emuinit, imod);
374 }
375 
376 int
377 readkbd(void)
378 {
379 	int n;
380 	char buf[1];
381 
382 	n = read(0, buf, sizeof(buf));
383 	if(n < 0)
384 		print("keyboard close (n=%d, %s)\n", n, strerror(errno));
385 	if(n <= 0)
386 		pexit("keyboard thread", 0);
387 
388 	switch(buf[0]) {
389 	case '\r':
390 		buf[0] = '\n';
391 		break;
392 	case DELETE:
393 		buf[0] = 'H' - '@';
394 		break;
395 	case CTRLC:
396 		cleanexit(0);
397 		break;
398 	}
399 	return buf[0];
400 }
401 
402 /*
403  * Return an abitrary millisecond clock time
404  */
405 long
406 osmillisec(void)
407 {
408 	static long sec0 = 0, usec0;
409 	struct timeval t;
410 
411 	if(gettimeofday(&t,(struct timezone*)0)<0)
412 		return 0;
413 
414 	if(sec0 == 0) {
415 		sec0 = t.tv_sec;
416 		usec0 = t.tv_usec;
417 	}
418 	return (t.tv_sec-sec0)*1000+(t.tv_usec-usec0+500)/1000;
419 }
420 
421 /*
422  * Return the time since the epoch in nanoseconds and microseconds
423  * The epoch is defined at 1 Jan 1970
424  */
425 vlong
426 osnsec(void)
427 {
428 	struct timeval t;
429 
430 	gettimeofday(&t, nil);
431 	return (vlong)t.tv_sec*1000000000L + t.tv_usec*1000;
432 }
433 
434 vlong
435 osusectime(void)
436 {
437 	struct timeval t;
438 
439 	gettimeofday(&t, nil);
440 	return (vlong)t.tv_sec * 1000000 + t.tv_usec;
441 }
442 
443 int
444 osmillisleep(ulong milsec)
445 {
446 	struct  timespec time;
447 
448 	time.tv_sec = milsec/1000;
449 	time.tv_nsec= (milsec%1000)*1000000;
450 	nanosleep(&time, NULL);
451 	return 0;
452 }
453 
454 int
455 limbosleep(ulong milsec)
456 {
457 	return osmillisleep(milsec);
458 }
459 
460 void
461 osyield(void)
462 {
463 	sched_yield();
464 }
465 
466 void
467 ospause(void)
468 {
469 	for(;;)
470 		pause();
471 }
472 
473 void
474 oslopri(void)
475 {
476 	setpriority(PRIO_PROCESS, 0, getpriority(PRIO_PROCESS,0)+4);
477 }
478 
479 static struct {
480 	Lock l;
481 	void *free;
482 } stacklist;
483 
484 static void
485 _stackfree(void *stack)
486 {
487 	*((void **)stack) = stacklist.free;
488 	stacklist.free = stack;
489 }
490 
491 static void
492 stackfreeandexit(void *stack)
493 {
494 	lock(&stacklist.l);
495 	_stackfree(stack);
496 	unlockandexit(&stacklist.l.val);
497 }
498 
499 static void *
500 stackalloc(Proc *p, void **tos)
501 {
502 	void *rv;
503 	lock(&stacklist.l);
504 	if (stacklist.free == 0) {
505 		int x;
506 		/*
507 		 * obtain some more by using sbrk()
508 		 */
509 		void *more = sbrk(KSTACK * (NSTACKSPERALLOC + 1));
510 		if (more == 0)
511 			panic("stackalloc: no more stacks");
512 		/*
513 		 * align to KSTACK
514 		 */
515 		more = (void *)((((unsigned long)more) + (KSTACK - 1)) & ~(KSTACK - 1));
516 		/*
517 		 * free all the new stacks onto the freelist
518 		 */
519 		for (x = 0; x < NSTACKSPERALLOC; x++)
520 			_stackfree((char *)more + KSTACK * x);
521 	}
522 	rv = stacklist.free;
523 	stacklist.free = *(void **)rv;
524 	unlock(&stacklist.l);
525 	*tos = rv + KSTACK - sizeof(vlong);
526 	*(Proc **)rv = p;
527 	return rv;
528 }
529