/* * AVR emulator * Emulator subprocess management * * Copyright (C) 2007 Michael Buesch * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "subprocess.h" #include "devprocess.h" #include #include #include #include #include #include #include #include #include static int fork_device_instance(struct avremu_host_instance *host) { struct avremu_device_instance *dev; int err; pid_t pid; struct avrmsg_to_host hostmsg; dev = avr_malloc(sizeof(*dev)); if (!dev) return -ENOMEM; dev->type = host->type; err = ipc_pipe_create(&host->msg_to_device, &dev->msg_from_host); if (err) { err = -ENOMEM; goto err_free_dev; } err = ipc_pipe_create(&dev->msg_to_host, &host->msg_from_device); if (err) { err = -ENOMEM; goto err_destroy1; } err = ipc_pipe_create(&dev->status_to_host, &host->status_from_device); if (err) { err = -ENOMEM; goto err_destroy2; } pid = fork(); if (pid == (pid_t)0) { /* DEVICE process */ ipc_pipe_destroy_tx(&host->msg_to_device); ipc_pipe_destroy_rx(&host->msg_from_device); ipc_pipe_destroy_rx(&host->status_from_device); host = NULL; /* Map stderr and stdout to the status pipe. * Also enable line buffering. */ ipc_dup2(&dev->status_to_host, STDERR_FILENO); ipc_dup2(&dev->status_to_host, STDOUT_FILENO); setvbuf(stdout, NULL, _IOLBF, 0); setvbuf(stderr, NULL, _IOLBF, 0); /* Run the device process. */ err = avr_device_process_run(dev); sendnotify_to_host(dev, HOSTMSG_EXIT); exit(err >= 0 ? err : -err); } else if (pid < (pid_t)0) { /* Failure */ err = -ENOMEM; goto err_destroy3; } else { /* HOST process */ ipc_pipe_destroy_tx(&dev->msg_to_host); ipc_pipe_destroy_rx(&dev->msg_from_host); ipc_pipe_destroy_tx(&dev->status_to_host); free(dev); dev = NULL; host->device_pid = pid; /* Wait for the device to initialize itself. */ while (1) { err = pollmsg_from_device(host, &hostmsg); if (err < 0) { err = -EIO; goto host_error; } if (err > 0) { if (hostmsg.msg == HOSTMSG_READY) break; if (hostmsg.msg == HOSTMSG_EXIT) { if (waitpid(pid, &err, 0) != pid) err = -ESRCH; else err = WEXITSTATUS(err); goto host_error; } } } host->device_running = 1; return 0; host_error: ipc_pipe_destroy_tx(&host->msg_to_device); ipc_pipe_destroy_rx(&host->msg_from_device); ipc_pipe_destroy_rx(&host->status_from_device); return err; } return 0; err_destroy3: ipc_pipe_destroy(&dev->status_to_host, &host->status_from_device); err_destroy2: ipc_pipe_destroy(&dev->msg_to_host, &host->msg_from_device); err_destroy1: ipc_pipe_destroy(&host->msg_to_device, &dev->msg_from_host); err_free_dev: free(dev); return err; } static void kill_device_instance(struct avremu_host_instance *host) { int err; printf("KILLING INSTANCE\n"); if (!host->device_running) return; printf("SEND TERM\n"); err = kill(host->device_pid, SIGTERM); if (err) { perror("Kill device process"); return; } printf("WAITING\n"); waitpid(host->device_pid, NULL, 0); host->device_running = 0; printf("WAITING DONE.\n"); } int avremu_instance_create(struct avremu_host_instance *host, enum avr_setup_type type) { int err; host->type = type; err = fork_device_instance(host); return err; } void avremu_instance_destroy(struct avremu_host_instance *host) { kill_device_instance(host); }