From: Melvin H. <mha...@us...> - 2002-12-15 18:25:39
|
Update of /cvsroot/libetpan/xetpan/src In directory sc8-pr-cvs1:/tmp/cvs-serv25328/src Modified Files: ui.c ui.h Log Message: a_UI_new(): take care of new members a_UI_init(): don't take the existance of a terminal for granted. Abort when no UI is available. a_UI_add_io_watch(): abstraction layer for IO multiplexing. a_UI_remove_io_watch(): idem a_UI_exec_cmd(): run a command and supply the 3 standard file descriptor Index: ui.c =================================================================== RCS file: /cvsroot/libetpan/xetpan/src/ui.c,v retrieving revision 1.5 retrieving revision 1.6 diff -u -d -r1.5 -r1.6 --- ui.c 13 Dec 2002 01:08:41 -0000 1.5 +++ ui.c 15 Dec 2002 18:25:36 -0000 1.6 @@ -25,6 +25,12 @@ #include <stdio.h> #include <string.h> #include <errno.h> +#include <unistd.h> /* pipe, dup2, write, close, fork, setpgid, getppid, + sysconf, execv */ +#include <sys/types.h> /* open, waitpid */ +#include <sys/stat.h> /* open */ +#include <fcntl.h> /* open */ +#include <sys/wait.h> /* waitpid */ #include "logging.h" #include "defines.h" @@ -34,11 +40,11 @@ #include "gtk-ui.h" #include "tty-ui.h" -/******************************************************************************/ -/* */ -/* UI basic */ -/* */ -/******************************************************************************/ +/****************************************************************************/ +/* */ +/* UI basic */ +/* */ +/****************************************************************************/ XePUI * a_UI_new(void) @@ -65,7 +71,17 @@ goto free_mainviews; } + ui->watched_fd = carray_new(10); + if (!ui->watched_fd) + { + A_LOGGING_ERROR("calloc: %s", strerror(errno)); + goto free_composewindows; + } + return ui; + + free_composewindows: + clist_free(ui->composewindows); free_mainviews: clist_free(ui->mainviews); free_ui: @@ -74,30 +90,37 @@ } /*! UI Initialization : - * If UI is specified, run only that UI, else use GUI if possible, else use - * TTY UI + * If UI is specified, run only that UI and abort if failed. + * If no UI is specified use GUI if possible, else use + * TTY UI. If no UI could be found, return -1. */ -void +int a_UI_init(XetPan *xetpan, int *argc, char ***argv) { + int ret = 0; + if (xetpan->ui->type == XETPAN_UI_GTK) - a_GtkUI_init(argc, argv); + a_GtkUI_init(xetpan, argc, argv); else if (xetpan->ui->type == XETPAN_UI_TTY) { - a_TtyUI_init(argc, argv); + a_TtyUI_init(xetpan, argc, argv); } else { - if (a_GtkUI_init_check(argc, argv)) + if (a_GtkUI_init_check(xetpan, argc, argv) == 0) { xetpan->ui->type = XETPAN_UI_GTK; } - else + else if (a_TtyUI_init_check(xetpan, argc, argv) == 0) { xetpan->ui->type = XETPAN_UI_TTY; - a_TtyUI_init(argc, argv); + a_TtyUI_init(xetpan, argc, argv); } + else + ret = -1; } + + return ret; } /* UI mainloop @@ -126,11 +149,11 @@ else a_TtyUI_main_quit(xetpan); } -/******************************************************************************/ -/* */ -/* UI: windows (GUI) and screens (TTY) */ -/* */ -/******************************************************************************/ +/****************************************************************************/ +/* */ +/* UI: windows (GUI) and screens (TTY) */ +/* */ +/****************************************************************************/ /* Display a welcome message, then the configurator, then eventually * compose messages, then display main view @@ -296,12 +319,12 @@ return a_XetPan_should_end(xetpan); } -/******************************************************************************/ -/* */ -/* UI remote command support */ -/* (Only the server process need these) */ -/* */ -/******************************************************************************/ +/****************************************************************************/ +/* */ +/* UI remote command support */ +/* (Only the server process need these) */ +/* */ +/****************************************************************************/ /* Server: Add a handler to accept remote client connections */ void @@ -343,3 +366,200 @@ a_TtyUI_send_cmd_result(xetpan); } + +/***************************************************************************/ +/* */ +/* Watching I/O traffic */ +/* */ +/***************************************************************************/ + +unsigned int +a_UI_add_io_watch(XetPan *xetpan, + int fd, + short int condition, + int (*callback)(int afd, short int acondition, void *adata), + void *data) +{ + if (xetpan->ui->type == XETPAN_UI_GTK) + return a_GtkUI_add_io_watch(xetpan, fd, condition, callback, data); + else + return a_TtyUI_add_io_watch(xetpan, fd, condition, callback, data); +} + +int +a_UI_remove_io_watch(XetPan *xetpan, unsigned int watch_id) +{ + if (xetpan->ui->type == XETPAN_UI_GTK) + return a_GtkUI_remove_io_watch(xetpan, watch_id); + else + return a_TtyUI_remove_io_watch(xetpan, watch_id); +} + +/***************************************************************************/ +/* */ +/* Executing shell commands */ +/* */ +/***************************************************************************/ + +int +a_UI_exec_cmd(char *cmd, int fd_in, int fd_out, int fd_err, int *fd_mon, + pid_t *pid, int sync) +{ + int child_mon[2]; + int ret; + + pid_t child_pid, gdchild_pid; + + if (!cmd) + { + A_LOGGING_ERROR("No command given."); + return -1; + } + + if (sync && !fd_mon) + { + A_LOGGING_ERROR("Synchroneous command must be monitored."); + return -1; + } + + child_mon[0] = child_mon[1] = -1; + + if (sync && pipe(child_mon)) + { + A_LOGGING_ERROR("pipe: %s. Command '%s' aborted.", strerror(errno), + cmd); + close(child_mon[0]); + close(child_mon[1]); + + return -1; + } + + child_pid = fork(); + + if (child_pid != 0) + { + /* Parent process executes this block */ + if (child_pid < 0) + { + /* Child forking did not succeed */ + A_LOGGING_ERROR("fork-ing child: %s. Command '%s' aborted.", + strerror(errno), + cmd); + if (sync) + { + close(child_mon[0]); + close(child_mon[1]); + } + ret = -1; + } + else + { + /* Child forking succeded */ + if (sync) + { + close(child_mon[1]); + *fd_mon = child_mon[0]; + if (pid) + *pid = child_pid; + } + else + waitpid(child_pid, NULL, 0); + ret = 0; + } + } + else + { + /* Child process executes this block */ + + int fd; + int max_fd; + int gdchild_status; + + if (setpgid(0, 0)) + A_LOGGING_ERROR("setpgid: %s", strerror(errno)); + + /* Close all file descriptors except */ + /* stdin, stdout, stderr, fd_in, fd_out, fd_err child_mon[1] */ + /* NB: if (sync), child_mon[1] == -1 */ + max_fd = sysconf(_SC_OPEN_MAX); + for (fd = 3; fd < max_fd; fd++) + if (fd != child_mon[1] && fd != fd_in && + fd != fd_out && fd != fd_err) + close(fd); + + gdchild_pid = fork(); + + if (gdchild_pid != 0) + { + /* Child process executes this block */ + if (gdchild_pid < 0) + { + /* Grand child forking error */ + A_LOGGING_ERROR("child process: fork-ing grand child: %s." + "Command '%s' aborted.", strerror(errno), cmd); + if (sync) + { + gdchild_status = 0; /**< Irrelevant, no grand child forked */ + write(child_mon[1], (char *) &gdchild_status, sizeof(int)); + close(child_mon[1]); + } + ret = 1; + } + else + { + /* Grand child forked succesfully */ + if (sync) + { + A_LOGGING_INFO("child process: waiting for grand child."); + + waitpid(gdchild_pid, &gdchild_status, 0); + + A_LOGGING_INFO("child process: grand child exited with " + "status %d", gdchild_status); + + write(child_mon[1], (char *) &gdchild_status, sizeof(int)); + close(child_mon[1]); + } + else + { + A_LOGGING_INFO("child process: grand child launched. " + "May it live happy."); + } + ret = 0; + } + _exit(ret); + } + else + { + /* Grand child executes this block */ + char *cmdline[4]; + + close(child_mon[1]); + + if (setpgid(0, getppid())) + A_LOGGING_ERROR("grand child process: setpgid: %s.", + strerror(errno)); + /* If fd_* are negative, duping is not done + and the command inherits parent's standard files */ + + dup2(fd_in, STDIN_FILENO); + dup2(fd_out, STDOUT_FILENO); + dup2(fd_err, STDERR_FILENO); + + cmdline[0] = "/bin/sh"; + cmdline[1] = "-c"; + cmdline[2] = cmd; + cmdline[3] = 0; + + execv(cmdline[0], cmdline); + + A_LOGGING_ERROR("grand child process: execv: %s. " + "Command '%s' aborted.", strerror(errno), cmd); + _exit(1); + /* End of grand child block */ + } + } + + return ret; + +} Index: ui.h =================================================================== RCS file: /cvsroot/libetpan/xetpan/src/ui.h,v retrieving revision 1.4 retrieving revision 1.5 diff -u -d -r1.4 -r1.5 --- ui.h 13 Dec 2002 01:08:41 -0000 1.4 +++ ui.h 15 Dec 2002 18:25:36 -0000 1.5 @@ -28,7 +28,7 @@ XePUI * a_UI_new(void); -void +int a_UI_init(XetPan *xetpan, int *argc, char ***argv); void @@ -69,5 +69,46 @@ void a_UI_send_cmd_result(XetPan *xetpan); + + +/*! Add a handler when traffic occurs on a file descriptor + * \param xetpan the main XetPan structure + * \param fd the file descriptor to watch + * \param condition type of conditio to watch XETPAN_IO_WATCH_READ, + * XETPAN_IO_WATCH_WRITE or XETPAN_IO_WATCH_EXCEPTION + * \param callback a function that is fed with the file descriptor, + * the condition occured and the supplied user data and that + * must return non zero to stop watching + * \param data the user supplied data that will be passed to the callback + * \return the unique ID that can be used to stop watching. + */ +unsigned int +a_UI_add_io_watch(XetPan *xetpan, + int fd, + short int condition, + int (*callback)(int afd, short int acondition, void *adata), + void *data); + +int +a_UI_remove_io_watch(XetPan *xetpan, unsigned int watch_id); + +/*! Execute a shell command + * Uses the double fork technique + * \param cmd the shell command + * \param cmd the command to be executed by /bin/sh -c + * \param fd_in the standard input for the command (-1 for stdin) + * \param fd_out the standard output for the command (-1 for stdout) + * \param fd_err the standard error for the command (-1 for stderr) + * \parem fd_mon a place where to store the file descriptor used + * to monitor the child execution + * \param pid a place to store the pid of the child process. To kill + * it, use its negative value to kill the group instead. + * \param sync non zero to run in synchroneous mode where the command + * is monitored + * \return process id of the child process. + */ +int +a_UI_exec_cmd(char *cmd, int fd_in, int fd_out, int fd_err, int *fd_mon, + pid_t *_pid, int sync); #endif |