/* * LASH * * Copyright (C) 2002 Robert Ham * * 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. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include #include #include #include #include #include #include #include #include #include #define XTERM_COMMAND_EXTENSION "&& sh || sh" #define LASH_SERVER_OPTION "--lash-server" #define LASH_PROJECT_OPTION "--lash-project" #define LASH_ID_OPTION "--lash-id" loader_t * loader_new() { loader_t *loader; int sockets[2]; int err; loader = lash_malloc0(sizeof(loader_t)); loader->loader_pid = -1; err = socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); if (err == -1) { fprintf(stderr, "%s: could not create unix socket pair: %s\n", __FUNCTION__, strerror(errno)); free(loader); return NULL; } loader->server_socket = sockets[0]; loader->loader_socket = sockets[1]; return loader; } void loader_destroy(loader_t * loader) { lash_comm_event_t event; int err; LASH_PRINT_DEBUG("sending close event to loader"); lash_comm_event_set_type(&event, LASH_Comm_Event_Close); err = lash_comm_send_event(loader->server_socket, &event); if (err < 0) { fprintf(stderr, "%s: error sending Close event to loader; a dangling child process is likely\n", __FUNCTION__); } LASH_PRINT_DEBUG("close event sent"); err = close(loader->server_socket); if (err == -1) { fprintf(stderr, "%s: error closing loader's server-side socket: %s\n", __FUNCTION__, strerror(errno)); } free(loader); } /* all args are enclosed in quotes */ static size_t get_command_size(int argc, char **argv) { size_t size = 0; int i; for (i = 0; i < argc; i++) size += strlen(argv[i]) + 3; size += strlen(XTERM_COMMAND_EXTENSION); return size + 1; } static void create_command(char *command_buffer, int argc, char **argv) { char *ptr; int i; ptr = command_buffer; for (i = 0; i < argc; i++) { sprintf(ptr, "\"%s\" ", argv[i]); ptr += strlen(ptr); } sprintf(ptr, "%s", XTERM_COMMAND_EXTENSION); } static void loader_exec_program_in_xterm(int argc, char **argv) { size_t command_size; char *command_buffer; char *xterm_argv[6]; command_size = get_command_size(argc, argv); command_buffer = lash_malloc(command_size); create_command(command_buffer, argc, argv); xterm_argv[0] = "xterm"; xterm_argv[1] = "-e"; xterm_argv[2] = "bash"; xterm_argv[3] = "-c"; xterm_argv[4] = command_buffer; xterm_argv[5] = NULL; /* execute it */ execvp("xterm", xterm_argv); fprintf(stderr, "%s: execing program '%s' in an xterm failed: %s\n", __FUNCTION__, command_buffer, strerror(errno)); exit(1); } static void loader_exec_program(loader_t * loader, lash_exec_params_t * params) { int err; char *project_opt; char *server_opt; char id_opt[sizeof(LASH_ID_OPTION) + 36]; char **argv; int argc = 0; int i; /* no longer anything to do with lashd */ err = setsid(); if (err == -1) { fprintf(stderr, "%s: could not create new process group: %s", __FUNCTION__, strerror(errno)); } /* change the working dir */ err = chdir(params->working_dir); if (err == -1) { fprintf(stderr, "%s: could not change directory to working dir '%s' for program '%s': %s\n", __FUNCTION__, params->working_dir, params->argv[0], strerror(errno)); } /* construct the args */ project_opt = lash_malloc(strlen(params->project) + 1 + strlen(LASH_PROJECT_OPTION) + 1); sprintf(project_opt, "%s=%s", LASH_PROJECT_OPTION, params->project); server_opt = lash_malloc(strlen(params->server) + 1 + strlen(LASH_SERVER_OPTION) + 1); sprintf(server_opt, "%s=%s", LASH_SERVER_OPTION, params->server); sprintf(id_opt, "%s=", LASH_ID_OPTION); uuid_unparse(params->id, id_opt + strlen(LASH_ID_OPTION) + 1); argc += params->argc + 3; argv = lash_malloc(sizeof(char *) * (argc + 1)); for (i = 0; i < params->argc; i++) argv[i] = params->argv[i]; argv[(i++)] = project_opt; argv[(i++)] = server_opt; argv[(i++)] = id_opt; argv[i] = NULL; #ifdef LASH_DEBUG { int l; printf("%s: executing command: ", __FUNCTION__); for (l = 0; argv[l]; l++) { printf("%s ", argv[l]); } putchar('\n'); } #endif LASH_DEBUGARGS("flags: %d", params->flags); if (params->flags & LASH_Terminal) { LASH_PRINT_DEBUG("execing in terminal"); loader_exec_program_in_xterm(argc, argv); } /* execute it */ execvp(params->argv[0], argv); fprintf(stderr, "%s: execing program '%s' failed: %s\n", __FUNCTION__, params->argv[0], strerror(errno)); exit(1); } void loader_execute(loader_t * loader, lash_exec_params_t * params) { pid_t pid; const char *program; program = params->argv[0]; pid = fork(); switch (pid) { case -1: fprintf(stderr, "%s: could not fork in order to exec program '%s': %s", __FUNCTION__, program, strerror(errno)); break; case 0: loader_exec_program(loader, params); default: LASH_DEBUGARGS("forked to run program '%s'", program); break; } } void loader_event(loader_t * loader, lash_comm_event_t * event) { switch (lash_comm_event_get_type(event)) { case LASH_Comm_Event_Close: LASH_PRINT_DEBUG("recieved close event, exiting") exit(0); case LASH_Comm_Event_Exec: loader_execute(loader, lash_comm_event_take_exec(event)); break; default: fprintf(stderr, "%s: recieved unknown event of type %d\n", __FUNCTION__, lash_comm_event_get_type(event)); break; } } void loader_run(loader_t * loader) { lash_comm_event_t *event; int err; signal(SIGTERM, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); /* don't need the server socket */ err = close(loader->server_socket); if (err == -1) { fprintf(stderr, "%s: could not close server socket: %s\n", __FUNCTION__, strerror(errno)); } /* apps don't need the loader socket */ err = fcntl(loader->loader_socket, F_SETFD, FD_CLOEXEC); if (err == -1) { fprintf(stderr, "%s: could not set close-on-exec on loader socket: %s\n", __FUNCTION__, strerror(errno)); } /* we don't care if apps finish */ signal(SIGCHLD, SIG_IGN); for (;;) { LASH_PRINT_DEBUG("listening for event"); event = lash_comm_event_new(); err = lash_comm_recv_event(loader->loader_socket, event); if (err == -2) { fprintf(stderr, "%s: server closed socket; exiting\n", __FUNCTION__); exit(1); } if (err == -1) { fprintf(stderr, "%s: error recieving event; exiting\n", __FUNCTION__); exit(1); } LASH_PRINT_DEBUG("got event"); loader_event(loader, event); lash_comm_event_destroy(event); } } int loader_fork(loader_t * loader) { pid_t pid; int err; pid = fork(); switch (pid) { case -1: fprintf(stderr, "%s: error while forking: %s\n", __FUNCTION__, strerror(errno)); return 1; case 0: loader_run(loader); exit(0); break; default: LASH_DEBUGARGS("loader running with pid %d", pid); loader->loader_pid = pid; err = close(loader->loader_socket); if (err == -1) { fprintf(stderr, "%s: error closing loader socket: %s\n", __FUNCTION__, strerror(errno)); } break; } return 0; } void loader_load(loader_t * loader, const lash_exec_params_t * params) { lash_comm_event_t event; int err; lash_comm_event_set_exec(&event, (lash_exec_params_t *) params); LASH_PRINT_DEBUG("sending exec params"); err = lash_comm_send_event(loader->server_socket, &event); if (err < 0) { fprintf(stderr, "%s: error sending event to the loader\n", __FUNCTION__); } LASH_PRINT_DEBUG("exec params sent"); } /* EOF */