[BACK]Return to main.c CVS log [TXT][DIR] Up to [local] / prex-old / usr / server / exec

File: [local] / prex-old / usr / server / exec / main.c (download)

Revision 1.1, Tue Jun 3 09:38:51 2008 UTC (15 years, 9 months ago) by nbrk
Branch point for: MAIN

Initial revision

/*-
 * Copyright (c) 2005-2007, Kohsuke Ohtani
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * Exec server - Execute various types of image files.
 */

#include <prex/prex.h>
#include <prex/capability.h>
#include <server/fs.h>
#include <server/proc.h>
#include <server/stdmsg.h>
#include <server/object.h>
#include <sys/list.h>

#include <limits.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#include "exec.h"

/*
 * Object for system server
 */
object_t proc_obj;	/* Process server */
object_t fs_obj;	/* File system server */

/*
 * File header
 */
static char header[HEADER_SIZE];

/*
 * Wait until specified server starts.
 */
static void
wait_server(const char *name, object_t *obj)
{
	int i, err = 0;

	/*
	 * Wait 1sec for loading server.
	 */
	for (i = 0; i < 100; i++) {
		err = object_lookup((char *)name, obj);
		if (err == 0)
			break;

		/* Wait 10msec */
		timer_sleep(10, 0);
		thread_yield();
	}
	if (err)
		sys_panic("exec: server not found");
}

static void
process_init(void)
{
	struct msg m;

	m.hdr.code = PS_REGISTER;
	msg_send(proc_obj, &m, sizeof(m));
}

/*
 * Notify exec() to servers.
 */
static void
notify_server(task_t org_task, task_t new_task, void *stack)
{
	struct msg m;
	int err;

	/* Notify to file system server */
	do {
		m.hdr.code = FS_EXEC;
		m.data[0] = org_task;
		m.data[1] = new_task;
		err = msg_send(fs_obj, &m, sizeof(m));
	} while (err == EINTR);

	/* Notify to process server */
	do {
		m.hdr.code = PS_EXEC;
		m.data[0] = org_task;
		m.data[1] = new_task;
		m.data[2] = (int)stack;
		err = msg_send(proc_obj, &m, sizeof(m));
	} while (err == EINTR);
}

/*
 * Execute program
 */
static int
do_exec(struct exec_msg *msg)
{
	struct exec_loader *ldr;
	char *name;
	int err, fd, count;
	struct stat st;
	task_t old_task, new_task;
	thread_t th;
	void *stack, *sp;
	void (*entry)(void);
	cap_t cap;

	dprintf("do_exec: path=%s task=%x\n", msg->path, msg->hdr.task);

	old_task = msg->hdr.task;

	/*
	 * Check capability of caller task.
	 */
	if (task_getcap(old_task, &cap)) {
		err = EINVAL;
		goto err1;
	}
	if ((cap & CAP_EXEC) == 0) {
		err = EPERM;
		goto err1;
	}
	/*
	 * Check target file
	 */
	if ((fd = open(msg->path, O_RDONLY)) == -1) {
		err = ENOENT;
		goto err1;
	}
	if (fstat(fd, &st) == -1) {
		err = EIO;
		goto err2;
	}
	if (!S_ISREG(st.st_mode)) {
		err = EACCES;	/* must be regular file */
		goto err2;
	}
	/*
	 * Find file loader from the file header.
	 */
	if ((count = read(fd, header, HEADER_SIZE)) == -1) {
		err = EIO;
		goto err2;
	}
	for (ldr = loader_table; ldr->name != NULL; ldr++) {
		if (ldr->probe(header) == 0)
			break;
		/* Check next format */
	}
	if (ldr->name == NULL) {
		dprintf("Unsupported file format\n");
		err = ENOEXEC;
		goto err2;
	}
	dprintf("exec loader=%s\n", ldr->name);

	/*
	 * Suspend old task
	 */
	if ((err = task_suspend(old_task)) != 0)
		goto err2;

	/*
	 * Create new task
	 */
	if ((err = task_create(old_task, VM_NEW, &new_task)) != 0)
		goto err2;

	if (msg->path[0] != '\0') {
		name = strrchr(msg->path, '/');
		if (name)
			name++;
		else
			name = msg->path;
		task_name(new_task, name);
	}
	/*
	 * Copy capabilities
	 */
	task_getcap(old_task, &cap);
	/* cap &= CONFIG_CAP_MASK; */
	task_setcap(new_task, &cap);

	if ((err = thread_create(new_task, &th)) != 0)
		goto err3;

	/*
	 * Allocate stack and build arguments on it.
	 */
	err = vm_allocate(new_task, &stack, USTACK_SIZE, 1);
	if (err)
		goto err4;
	if ((err = build_args(new_task, stack, msg, &sp)) != 0)
		goto err5;

	/*
	 * Load file image.
	 */
	if ((err = ldr->load(header, new_task, fd, (void **)&entry)) != 0)
		goto err5;
	if ((err = thread_load(th, entry, sp)) != 0)
		goto err5;

	/*
	 * Notify to servers.
	 */
	notify_server(old_task, new_task, stack);

	/*
	 * Terminate old task.
	 */
	task_terminate(old_task);

	/*
	 * Set him running.
	 */
	thread_resume(th);

	close(fd);
	dprintf("exec complete successfully\n");
	return 0;
 err5:
	vm_free(new_task, stack);
 err4:
	thread_terminate(th);
 err3:
	task_terminate(new_task);
 err2:
	close(fd);
 err1:
	dprintf("exec failed err=%d\n", err);
	return err;
}

/*
 * Debug
 */
static void
exec_debug(void)
{

#ifdef DEBUG
	/* mstat(); */
#endif
}

/*
 * Initialize all exec loaders
 */
static void
exec_init(void)
{
	struct exec_loader *ldr;

	for (ldr = loader_table; ldr->name; ldr++) {
		dprintf("Initialize \'%s\' loader\n", ldr->name);
		ldr->init();
	}
}

/*
 * Main routine for exec service.
 */
int
main(int argc, char *argv[])
{
	struct exec_msg msg;
	object_t obj;
	int err;

	sys_log("Starting Exec Server\n");

	/*
	 * Boost current priority
	 */
	thread_setprio(thread_self(), PRIO_EXEC);

	/*
	 * Wait until system server becomes available.
	 */
	wait_server(OBJNAME_PROC, &proc_obj);
	wait_server(OBJNAME_FS, &fs_obj);

	/*
	 * Register to process server
	 */
	process_init();

	/*
	 * Register to file server
	 */
	fslib_init();

	/*
	 * Initialize everything
	 */
	exec_init();

	/*
	 * Create an object to expose our service.
	 */
	err = object_create(OBJNAME_EXEC, &obj);
	if (err)
		sys_panic("fail to create object");

	/*
	 * Message loop
	 */
	for (;;) {
		/*
		 * Wait for an incoming request.
		 */
		err = msg_receive(obj, &msg, sizeof(struct exec_msg));
		if (err)
			continue;
		/*
		 * Process request.
		 */
		err = EINVAL;
		switch (msg.hdr.code) {
		case STD_DEBUG:
			exec_debug();
			err = 0;
			break;

		case EX_EXEC:
			err = do_exec(&msg);
			break;
		}
#ifdef DEBUG_EXEC
		if (err)
			dprintf("msg error=%d\n", err);
#endif
		/*
		 * Reply to the client.
		 */
		msg.hdr.status = err;
		err = msg_reply(obj, &msg, sizeof(struct exec_msg));
	}
	return 0;
}