454 lines · 10.3 KB · C
RawDownload
#include "inc/priv.h"#include "inc/cvalues.h"#include "inc/print.h"#include "inc/io.h"#include "inc/process.h"#include "inc/types.h" static const sl_type *processtype;static sl_v runningsym, exitedsym, stoppedsym, signaledsym;static sl_v iosym, insym, outsym, errsym, envsym, dirsym;static const int iosflags[] = {	IOS_READ,	IOS_CREATE|IOS_WRITE|IOS_TRUNC,	IOS_CREATE|IOS_WRITE|IOS_TRUNC};static int nullfd[3]; typedef struct sl_process sl_process; struct sl_pid {	int pid;	int code;	bool dumped;	sl_v state;	char *msg;	sl_pid *next; // FIXME(sigrid): binary tree}; struct sl_process {	sl_pid *pid;	sl_v stream[3];}; static boolisprocess(sl_v v){	return iscvalue(v) && cvalue_class(v) == processtype;} static sl_process *toprocess(sl_v v){	if(sl_likely(isprocess(v)))		return cvalue_data(v);	cthrow(type_error("value", "process", v), v);} BUILTIN("find-program", find_program){	argcount(1);	char *path = sys_find_program(tostr(args[0]));	if(path == nil)		return sl_nil;	sl_v v = str_from_cstr(path);	MEM_FREE(path);	return v;} BUILTIN("process?", processp){	argcount(1);	return isprocess(args[0]);} BUILTIN("process-alive?", process_alivep){	argcount(1);	sl_pid *pid = toprocess(args[0])->pid;	return pid->state != runningsym && pid->state != stoppedsym ? sl_nil : sl_t;} BUILTIN("process-core-dumped?", process_code_dumpedp){	argcount(1);	sl_pid *pid = toprocess(args[0])->pid;	return pid->dumped ? sl_t : sl_nil;} BUILTIN("process-state", process_state){	argcount(1);	return toprocess(args[0])->pid->state;} BUILTIN("process-exit-code", process_exit_code){	argcount(1);	sl_process *proc = toprocess(args[0]);	if(proc->pid->code < 0)		return sl_nil;	return fixnum(proc->pid->code);} BUILTIN("process-exit-msg", process_exit_msg){	argcount(1);	sl_process *proc = toprocess(args[0]);	if(proc->pid == nil || proc->pid->code < 0)		return sl_nil;	if(proc->pid->code == 0 || proc->pid->msg == nil)		return sl_emptystr;	return str_from_cstr(proc->pid->msg);} static sl_vprocess_stream(sl_v p, int n){	sl_process *proc = toprocess(p);	return proc->stream[n];} BUILTIN("process-input", process_input){	argcount(1);	return process_stream(args[0], 0);} BUILTIN("process-output", process_output){	argcount(1);	return process_stream(args[0], 1);} BUILTIN("process-error", process_error){	argcount(1);	return process_stream(args[0], 2);} BUILTIN("make-process", make_process){	const char *const fds[] = {"in", "out", "err"};	sl_v keys[] = {insym, outsym, errsym, envsym, dirsym};	sl_v values[nelem(keys)];	nargs = sl_process_args(args, 2, nargs, keys, nelem(keys), values);	argcount(2); 	int argc = 0, fd[3];	int closefd[3], numclosefd = 0;	int chclosefd[3], numchclosefd = 0;	sl_v stream[3] = {sl_nil, sl_nil, sl_nil};	char *program = MEM_STRDUP(tostr(args[0])), **argv = nil, **env = nil;	sl_pid *pid = nil;	int dirfd = -1; 	for(int i = 0; i < nelem(values); i++)		sl_gc_handle(&values[i]);	sl_gc_handle(&stream[0]);	sl_gc_handle(&stream[1]);	sl_gc_handle(&stream[2]);	sl_v v = sl_nil, err = sl_nil; 	SL_TRY{		// collect argv		sl_v a = args[1];		(void)tocons(a);		argc = llength(args[1]);		if((argv = MEM_ALLOC((argc+1)*sizeof(char*))) == nil)			bthrow(error(sl_errmem, "no memory for argv"));		for(int i = 0; i < argc; i++, a = cdr_(a)){			argv[i] = nil;			if(!iscons(a))				bthrow(type_error("argv", "cons", a));			sl_v s = car_(a);			if(!isstr(s))				bthrow(type_error("arg", "str", s));			if((argv[i] = MEM_STRDUP(tostr(s))) == nil)				bthrow(error(sl_errmem, "no memory for argv[%d]", i));		}		argv[argc] = nil; 		// collect env		sl_v e = values[3];		if(e != sl_unbound){			int ne = llength(e);			if(e != sl_nil)				(void)tocons(e);			if((env = MEM_ALLOC((ne+1)*sizeof(char*))) == nil)				bthrow(error(sl_errmem, "no memory for env"));			for(int i = 0; i < ne; i++, e = cdr_(e)){				env[i] = nil;				if(!iscons(e))					bthrow(type_error("env", "cons", e));				sl_v pair = car_(e);				if(!iscons(pair))					bthrow(type_error("env", "assoc list", e));				const char *ek = tostr(car_(pair)), *ev = tostr(cdr_(pair));				int nk = strlen(ek), nv = strlen(ev), sz = nk+1+nv+1;				if((env[i] = MEM_ALLOC(nk+1+nv+1)) == nil)					bthrow(error(sl_errmem, "no memory for env[%d]", i));				snprintf(env[i], sz, "%s=%s", ek, ev);			}			env[ne] = nil;		} 		// get the streams ready		for(int i = 0; i < nelem(fd); i++){			sl_v io = values[i];			if(io == sl_t)				fd[i] = i;			else if(io == sl_unbound || io == sl_nil)				fd[i] = nullfd[i];			else if(io == outsym && i == 2)				fd[i] = fd[1];			else if(io == iosym){				stream[i] = cvalue(sl_iotype, sizeof(ios));				ios *s = value2c(ios*, stream[i]);				ios_pipe(s, iosflags[i] & IOS_RDWR, &fd[i]);				if(fd[i] < 0)					bthrow(error(sl_errio, "%s: pipe failed", fds[i]));				closefd[numclosefd++] = fd[i];				chclosefd[numchclosefd++] = s->fd;			}else if(isio(io)){				ios *s = toio(					io,					true,					(iosflags[i] & IOS_READ) != 0,					(iosflags[i] & IOS_WRITE) != 0				);				if(s->fd < 0)					bthrow(error(sl_errio, "%s: memory buffers not supported yet", fds[i])); // FIXME(sigrid)				fd[i] = s->fd;			}else if(isstr(io)){				fd[i] = sys_open(tostr(io), iosflags[i], 0644);				if(fd[i] < 0)					bthrow(error(sl_errio, "%s: could not open %s", fds[i], tostr(v)));				closefd[numclosefd++] = fd[i];			}else				bthrow(error(sl_errarg, "%s: invalid stream", fds[i]));		} 		// init the pid		if((pid = MEM_ALLOC(sizeof(sl_pid))) == nil)			bthrow(error(sl_errmem, "no memory for pid"));		pid->pid = -1;		pid->code = -1;		pid->msg = nil;		pid->state = runningsym;		pid->dumped = false;		pid->next = slvm.pids;		slvm.pids = pid; 		const sl_v dir = values[4]; // :dir		const char *dirpath = nil;		if(dir != sl_unbound && dir != sl_nil){			dirpath = tostr(dir);			dirfd = sys_open(dirpath, IOS_DIR, 0);			if(dirfd < 0)				bthrow(error(sl_errarg, "dir: bad directory: %s", dirpath));		} 		// fork, close/deallocate parent's resources		int chpid = sys_fork(env == nil ? 0 : FORK_CLEAR_ENV);		if(chpid < 0)			bthrow(error(sl_err, "fork failed"));		if(chpid > 0){ // parent			pid->pid = chpid;			v = cvalue(processtype, sizeof(sl_process));			sl_process *process = value2c(sl_process*, v);			process->pid = pid;			for(int i = 0; i < nelem(fd); i++)				process->stream[i] = stream[i];		}else{ // child			for(int i = 0; i < numchclosefd; i++)				sys_close(chclosefd[i]);			if(dirfd >= 0){				if(sys_chdir(dirfd, dirpath) != 0)					goto child_failed;				sys_close(dirfd);			}			for(int i = 0; i < nelem(fd); i++){				if(sys_dup(fd[i], i) < 0)					goto child_failed;			}			sys_exec(program, argv, env);child_failed:#if defined(__plan9__)			sysfatal("make-process: %r");#else			// FIXME(sigrid): check errno? pass it on to the parent?			sysfatal("make-process failed");#endif		}	}	SL_CATCH{		for(int i = 0; i < nelem(stream); i++){			if(isio(stream[i]))				ios_close(value2c(ios*, stream[i]));		}		MEM_FREE(pid);		err = sl.lasterror;	} 	if(dirfd >= 0)		sys_close(dirfd);	for(int i = 0; i < numclosefd; i++)		sys_close(closefd[i]);	sl_free_gc_handles(nelem(values)+3); 	MEM_FREE(program);	for(int i = 0; i < argc; i++)		MEM_FREE(argv[i]);	MEM_FREE(argv);	if(env != nil){		for(int i = 0; env[i] != nil; i++)			MEM_FREE(env[i]);		MEM_FREE(env);	} 	if(err != sl_nil)		sl_raise(err);	return v;} static voidprint_process(sl_v v){	sl_process *proc = toprocess(v);	sl_print_str("#<process ", true);	sl_print(fixnum(proc->pid->pid));	sl_v state = proc->pid->state;	if(state != sl_nil){		sl_print_chr(' ');		sl_print(state);		if(state != runningsym && state != stoppedsym && proc->pid->code != 0){			sl_print_chr(' ');			sl_print(fixnum(proc->pid->code));			if(proc->pid->msg != nil){				sl_print_str(" \"", true);				sl_print_str(proc->pid->msg, false);				sl_print_chr('"');				if(proc->pid->dumped)					sl_print_str(" dumped", true);			}		}	}	sl_print_chr('>');} static voidrelocate_process(sl_v oldv, sl_v newv){	sl_process *p = toprocess(newv);	for(int i = 0; i < nelem(p->stream); i++)		p->stream[i] = sl_relocate(p->stream[i]);	USED(oldv);} static voidfree_process(sl_v self){	sl_process *proc = value2c(sl_process*, self);	sl_pid *p, *prev = nil;	for(p = slvm.pids; p != nil; prev = p, p = p->next){		if(p == proc->pid){			if(prev == nil)				slvm.pids = p->next;			else				prev->next = p->next;			break;		}	}	if(p == nil)		abort();	MEM_FREE(p->msg);	MEM_FREE(p);	proc->pid = nil;} BUILTIN("process-close", process_close){	argcount(1);	sl_process *proc = toprocess(args[0]);	for(int i = 0; i < nelem(proc->stream); i++){		if(isio(proc->stream[i]))			ios_close(value2c(ios*, proc->stream[i]));	}	return sl_void;} BUILTIN("process-post", process_post){	argcount(3);	sl_process *proc = toprocess(args[0]);	const char *note = tostr(args[1]);	const char *sig = tosym(args[2])->name;	return sys_postnote(proc->pid->pid, note, sig) == 0 ? sl_t : sl_nil;} static sl_cvtable process_vtable = {	print_process,	relocate_process,	free_process,	nil,	nil,}; voidprocess_update(int pid, process_state_t state, int code, const char *msg){	for(sl_pid *p = slvm.pids; p != nil; p = p->next){		if(p->pid >= 0 && p->pid != pid)			continue;		switch(state){		case PSTATE_EXITED:			p->state = exitedsym;			break;		case PSTATE_STOPPED:			p->state = stoppedsym;			break;		case PSTATE_DUMPED:			p->dumped = true;			sl_fallthrough;		case PSTATE_SIGNALED:			p->state = signaledsym;			break;		case PSTATE_RUNNING:			p->state = runningsym;			break;		}		p->code = code;		p->msg = msg == nil ? nil : MEM_STRDUP(msg);		slvm.pidupdates++; // FIXME(sigrid): atomic		break;	}	// GC'ed - nobody cares anymore} voidprocess_init(void){	if((nullfd[0] = sys_open(SYS_DEV_NULL, IOS_READ, 0)) < 0)		sysfatal("%s", SYS_DEV_NULL);	if((nullfd[1] = nullfd[2] = sys_open(SYS_DEV_NULL, IOS_WRITE, 0)) < 0)		sysfatal("%s", SYS_DEV_NULL); 	processtype = define_opaque_type(		mk_sym("process", false),		sizeof(sl_process),		&process_vtable,		nil	); 	runningsym = mk_csym(":running");	exitedsym = mk_csym(":exited");	stoppedsym = mk_csym(":stopped");	signaledsym = mk_csym(":signaled");	insym = mk_csym(":in");	outsym = mk_csym(":out");	errsym = mk_csym(":err");	envsym = mk_csym(":env");	iosym = mk_csym(":io");	dirsym = mk_csym(":dir");}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t