/* * ep - extended pipelining * * main.c - main implemetation * ____________________________________________________________ * * Developed by Ondrej Jombik * and Lubomir Host * Copyright (c) 2000-2003 Platon SDG, http://platon.sk/ * All rights reserved. * * See README file for more information about this software. * See COPYING file for license information. * * Download the latest version from * http://platon.sk/projects/ep/ */ /* $Platon: ep/src/main.c,v 1.34 2003/07/16 16:09:13 nepto Exp $ */ /* * ep - extended pipelining * coding start date: 11.11.2000 * authors: Ondrej Jombik * Lubomir Host * Martin Karas * Copyright (c) 2000-2003 Platon Software Development Group * license: GNU General Public License */ /* Notes: * Now signal handler for every signal is installed. If SIG_CHLD detected, gloal variable sig_chld is set. This change is printed out in main() in debug mode. No other usage of sig_chld variable is currently used. * When write() syscall fails (that means returning -1 or 0), particular proclinks to failed fd are removed and fd is closed. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #ifdef STDC_HEADERS # include # include #endif #if HAVE_UNISTD_H # include # include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #if HAVE_SYS_WAIT_H # include #endif #ifndef WEXITSTATUS # define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) #endif #ifndef WIFEXITED # define WIFEXITED(stat_val) (((stat_val) & 255) == 0) #endif #if MENU # include #endif #include "main.h" #include "process.h" #include "proctable.h" #include "fd.h" #include "message.h" #include "sigserv.h" #include "conf.h" #if MENU # include "menu.h" #endif /* * Global variables */ /* Global pointer to argv[0] (program name). */ char *argv0; /* Configration filenames (shouldn't be static in conf.c?). */ char *conf_global_filename = "/etc/ep.conf"; char *conf_user_filename = ".ep.conf"; /* Program configuration structure. */ struct conf_struct_global cfg; void exit_fnc(void) { conf_destroy(); #if DEBUG msg_debug_print("exit_fnc(): %s terminating\n", argv0); #endif free(argv0); #if MENU endwin(); #endif } int main(argc, argv) int argc; char **argv; { register int i, k; register int ret, pid = 0; struct timeval tv; fd_set rfds; fd_set wfds; fd_set efds; PROCTABLE ptbl; /* * Creating global variable argv0. */ argv0 = strrchr(argv[0], '/') == NULL ? strdup(argv[0]) : strdup(strrchr(argv[0], '/') + 1); if (argv0 == NULL) { fprintf(stderr, "fatal-error: not enough memory\n"); return 1; } /* * Registering atexit() function. */ if (atexit(exit_fnc) < 0) { msg_error_print("cannot register atexit() function\n"); return 1; } if (argc < 2) { msg_usage_print(); return 1; } #if MENU /* * Ncurses menu initialization */ menu_curses_init(); #endif /* * Initializing configuration module */ if (! conf_init()) { msg_error_print("cannot init configuration parsing structures\n"); return 1; } /* * Parsing command line and all config files (global, user and specified) */ ptbl = conf_parse_all(argc, argv); if (ptbl == NULL) { return 1; } #if DEBUG else msg_debug_print("configuration parsing: OK\n"); #endif if (/* proctable_add_std_process(&ptbl) < 0 ||*/ proctable_update(ptbl) < 0) { return 1; } #if DEBUG else msg_debug_print("proctable check: OK\n"); #endif /* * Creating pipes via pipe() syscalls */ if (proctable_create_pipes(ptbl) < 0) { msg_error_print("cannot create pipes\n"); return 1; } #if DEBUG else msg_debug_print("creating pipes: OK\n"); #endif /* * Fork processes and dup pipes. * Exec the processes via execvp(). */ if (proctable_exec(ptbl) < 0) { msg_error_print("fork() failure: %s\n", strerror(errno)); return 1; } #if DEBUG else msg_debug_print("%d times fork(): OK\n", proctable_get_size(ptbl)); #endif /* * Closing 2nd sides of pipes. */ proctable_close_2nd_sides_of_pipes(ptbl); /* * Instaling signal handler. */ sigserv_init(ptbl); #if DEBUG /* * Proctable print. */ proctable_print(ptbl); #endif /* * Main loop */ while (1) { register int size; /* Set rfds and efds. */ FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); for (i = 0; ptbl[i] != NULL; i++) { for (k = 0; k < N_FD; k++) { if (ptbl[i]->fd[k].n_link != 0) { if ((!i && !k) || (i && k)) { FD_SET(ptbl[i]->fd[k].pipe[0], &rfds); } else { /* FD_SET(ptbl[i]->fd[k].pipe[1], &wfds); */ } /* FD_SET(ptbl[i]->fd[k].pipe[k ? 0 : 1], &efds); */ } } } /* TODO: if closed fd 0, always use timeout */ if (pid == 1) { tv.tv_sec = 0; tv.tv_usec = 1000; } #if DEBUG if (sig_chld) { msg_debug_print("main(): SIG_CHLD detected\n"); sig_chld = 0; } #endif ret = select(FD_SETSIZE, &rfds, &wfds, &efds, pid == 1 ? &tv : NULL); if (ret < 0) { /* NOTE: this stuff around EBADF will be probably neccessary in the future. Although this, it is commented now, for better extended pipelinign debugging. */ if (1 || errno != EBADF) { msg_error_print("select() failure: %s\n", strerror(errno)); break; } sleep(1); } #if DEBUG /*if (FD_ISSET(0, &rfds) && getchar() == 12) proctable_print(ptbl);*/ #endif #if MENU if (FD_ISSET(0, &rfds)) { int c; c = getch(); switch (c) { case '\033': /* ESC */ case KEY_F(9): /* F9 */ menu_main(&ptbl); touchwin(stdscr); refresh(); break; case 12: /* ^l */ touchwin(stdscr); refresh(); break; default: break; } } #endif /* #if HAVE_MENU */ /* * Read and transfer data. */ if (ret > 0) { while (proctable_read(&ptbl, &rfds, &wfds, &efds) == 0) { #if DEBUG msg_debug_print("main(): repeating proctable_read()\n"); #endif } } /* * Check if processes still running. */ while (1) { pid = proctable_wait2(&ptbl); /* printf("pid = %d\n", pid);sleep(1); */ if (pid < 0) { msg_error_print("waitpid() failure: %s\n", strerror(errno)); break; } size = proctable_get_size(ptbl); /* printf("size = %d\n", size);sleep(1); */ if (size < 2 /* and 0 is closed */) { pid = -1; break; } if (pid == 0 || pid == 1) break; /* if (pid == 1) { if (proctable_wait(&ptbl) <= 1) break; } */ } if (pid < 0) break; } /* while (1) */ /* * Destroying used process table. */ proctable_destroy(&ptbl); return errno == 0 ? 0 : (errno < 0 ? -errno : errno); }