File: [Platon] / ep / src / proctable.c (download)
Revision 1.34, Fri Nov 28 17:35:11 2003 UTC (20 years, 4 months ago) by nepto
Changes since 1.33: +3 -3
lines
Changed URL from www.platon.sk to platon.sk.
|
/*
* ep - extended pipelining
*
* proctable.c - process table implementation
* ____________________________________________________________
*
* Developed by Ondrej Jombik <nepto@platon.sk>
* and Lubomir Host <rajo@platon.sk>
* 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/proctable.c,v 1.33 2003/05/03 09:58:19 nepto Exp $ */
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdio.h>
#include <errno.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <string.h>
#endif
#if HAVE_UNISTD_H
# include <sys/types.h>
# include <unistd.h>
#endif
#if HAVE_SYS_WAIT_H
# include <sys/wait.h>
#endif
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h> /* S_IFCHR */
/* #include <sys/time.h> PRIO_PROCESS */
#include <sys/resource.h> /* PRIO_PROCESS */
#include "platon-str.h"
#include "proctable.h"
#include "process.h"
#include "message.h" /* msg_*_print() */
#include "conf.h" /* cfg */
static char buf[BUFSIZE];
int
proctable_get_size(ptbl)
PROCTABLE ptbl;
{
register int i;
for (i = 0; ptbl[i] != NULL; i++)
;
return i;
}
PROCTABLE
proctable_create(size)
const int size;
{
PROCTABLE ptbl;
register int i;
ptbl = (PROCTABLE) malloc((size + 1) * sizeof(struct process *));
if (ptbl == NULL)
return NULL;
ptbl[size] = NULL;
for (i = 0; i < size; i++) {
ptbl[i] = (struct process *) malloc(sizeof(struct process));
if (ptbl[i] == NULL) {
proctable_destroy(&ptbl);
return NULL;
}
process_init(ptbl[i]);
}
return ptbl;
}
void
proctable_destroy(p_ptbl)
PROCTABLE *p_ptbl;
{
if (p_ptbl != NULL) {
register int i;
/* TODO:
* Here was written *p_ptbl[i] instead (*p_ptbl)[i] on several places.
* I supposed that it is the same. LEARN OPERATOR PRIORITIES ASAP!!
* -- Nepto [2/12/2001]
*/
for (i = 0; (*p_ptbl)[i] != NULL; i++) {
process_destroy((*p_ptbl)[i]);
free((*p_ptbl)[i]);
}
free(*p_ptbl);
*p_ptbl = NULL;
}
}
int
proctable_join(p_dest_ptbl, src_ptbl)
PROCTABLE *p_dest_ptbl;
PROCTABLE src_ptbl;
{
register int i, dest_size, src_size;
if (p_dest_ptbl == NULL)
return 0;
for (i = 0; *p_dest_ptbl != NULL && (*p_dest_ptbl)[i] != NULL; i++)
;
dest_size = i;
for (i = 0; src_ptbl != NULL && src_ptbl[i] != NULL; i++)
;
src_size = i;
*p_dest_ptbl = (PROCTABLE) realloc(*p_dest_ptbl,
sizeof(struct process *) * (dest_size + src_size + 1));
if (*p_dest_ptbl == NULL)
return 0;
for (i = 0; i < src_size; i++)
(*p_dest_ptbl)[dest_size + i] = src_ptbl[i];
(*p_dest_ptbl)[dest_size + src_size] = NULL;
return 1;
}
int
proctable_get_process_index_by_pid(ptbl, pid)
PROCTABLE ptbl;
int pid;
{
register int i = 0;
for (i = 0; ptbl[i] != NULL; i++)
if (ptbl[i]->pid == pid)
return i;
return -1;
}
int
proctable_get_process_index_by_name(ptbl, name)
PROCTABLE ptbl;
char *name;
{
register int i = 0;
for (i = 0; ptbl[i] != NULL; i++)
if (ptbl[i]->name != NULL
&& ! PLATON_FUNC(strdyn_casecmp)(ptbl[i]->name, name))
return i;
return -1;
}
int
proctable_add_std_process(p_ptbl)
PROCTABLE *p_ptbl;
{
register int i;
i = proctable_get_size(*p_ptbl);
/* 1 for ending NULL, 1 for new std_process */
*p_ptbl = (PROCTABLE) realloc(*p_ptbl, (i + 2) * sizeof(struct process *));
if (*p_ptbl == NULL) {
msg_error_print("realloc() failure\n");
return -1;
}
for (; i >= 0; i--)
(*p_ptbl)[i + 1] = (*p_ptbl)[i];
(*p_ptbl)[0] = (struct process *) malloc(sizeof(struct process));
if ((*p_ptbl)[0] == NULL) {
msg_error_print("malloc() failure\n");
for (i = 1; *p_ptbl[i] != NULL; i++) {
process_destroy(*p_ptbl[i]);
free(*p_ptbl[i]);
}
free(*p_ptbl);
return -1;
}
process_init((*p_ptbl)[0]);
return 0;
}
int
proctable_update(ptbl)
PROCTABLE ptbl;
{
register int i, j, k;
struct proclink link;
for (i = 0; ptbl[i] != NULL; i++) {
link.proc_id = i;
for (k = 0; k < N_FD; k++) {
link.fd_id = k;
for (j = 0; j < ptbl[i]->fd[k].n_link; j++) {
/* Invalid link? */
if (ptbl[i]->fd[k].link[j].proc_id
>= proctable_get_size(ptbl)) {
msg_error_print("invalid process number '%d'\n",
ptbl[i]->fd[k].link[j].proc_id);
return -1;
}
/* Selflink? */
if (i == ptbl[i]->fd[k].link[j].proc_id) {
msg_error_print("cannot self link from %d.%s to %d.%s\n",
i, fd_id_to_fd_str(k),
ptbl[i]->fd[k].link[j].proc_id,
fd_id_to_fd_str(ptbl[i]->fd[k].link[j].fd_id));
return -1;
}
/* Duplicated link? */
if (fd_is_proclink(&ptbl[i]->fd[k],
&ptbl[i]->fd[k].link[j]) - 1 != j) {
msg_error_print(
"duplicated link from %d.%s to %d.%s\n",
i, fd_id_to_fd_str(k),
ptbl[i]->fd[k].link[j].proc_id,
fd_id_to_fd_str(ptbl[i]->fd[k].link[j].fd_id));
return -1;
}
/* Linking input to input or output to output? */
while (1) {
if (k == 0) {
if (i == 0) {
if (ptbl[i]->fd[k].link[j].fd_id == 0)
break;
}
else
if (ptbl[i]->fd[k].link[j].fd_id == 0) {
if (ptbl[i]->fd[k].link[j].proc_id == 0)
break;
}
else
if (ptbl[i]->fd[k].link[j].proc_id != 0)
break;
}
else {
if (i == 0) {
if (ptbl[i]->fd[k].link[j].fd_id != 0)
break;
}
else
if (ptbl[i]->fd[k].link[j].fd_id != 0) {
if (ptbl[i]->fd[k].link[j].proc_id == 0)
break;
}
else
if (ptbl[i]->fd[k].link[j].proc_id != 0)
break;
}
msg_error_print(
"incorrect link from %d.%s to %d.%s\n",
i, fd_id_to_fd_str(k),
ptbl[i]->fd[k].link[j].proc_id,
fd_id_to_fd_str(ptbl[i]->fd[k].link[j].fd_id));
return -1;
}
/* If equivalent link doesn't exist, create it. */
if (fd_is_proclink(&ptbl[ptbl[i]->fd[k].link[j].proc_id]->
fd[ptbl[i]->fd[k].link[j].fd_id], &link))
;
else
fd_add_proclink(&ptbl[ptbl[i]->fd[k].link[j].proc_id]->
fd[ptbl[i]->fd[k].link[j].fd_id], &link);
}
}
}
return 0;
}
int
proctable_create_pipes(ptbl)
PROCTABLE ptbl;
{
register int i, k;
for (i = 0; ptbl[i] != NULL; i++) {
for (k = 0; k < N_FD; k++) {
/* XXX: We want to create pipes anyway. */
if (1 || ptbl[i]->fd[k].n_link != 0) {
if (i == 0) {
/* Set the standart (basic) descriptors. */
ptbl[0]->fd[k].pipe[0] = ptbl[0]->fd[k].pipe[1] = k;
}
else {
#if DEBUG
msg_debug_print("creating pipe ptbl[%d]->fd[%d].pipe",
i, k);
#endif
if (pipe(ptbl[i]->fd[k].pipe) < 0) {
#if DEBUG
msg_print(" - failed\n");
#endif
return -1;
}
#if DEBUG
msg_print(" = (%d, %d)\n",
ptbl[i]->fd[k].pipe[0], ptbl[i]->fd[k].pipe[1]);
#endif
}
}
}
}
return 0;
}
int
proctable_exec(ptbl)
PROCTABLE ptbl;
{
register int i, j, k;
for (i = 1; ptbl[i] != NULL; i++) {
switch (ptbl[i]->pid = fork()) {
/* Parent process. */
default:
break;
/* Error. */
case -1:
return -1;
/* Child process. */
case 0:
for (k = 0; k < N_FD; k++)
/* XXX: We want to dup pipe anyway. */
if (1 || ptbl[i]->fd[k].n_link != 0) {
dup2(ptbl[i]->fd[k].pipe[k ? 1 : 0], k);
/* See the '1 ||' below.
close(ptbl[i]->fd[k].pipe[0]);
close(ptbl[i]->fd[k].pipe[1]);
*/
}
else {
#if DEBUG
msg_debug_print("proctable_exec(): "
"process %d, fd %d, no links defined\n", i, k);
#endif
}
for (j = 1; ptbl[j] != NULL; j++) {
if (1 || i != j)
for (k = 0; k < N_FD; k++) {
close(ptbl[j]->fd[k].pipe[0]);
close(ptbl[j]->fd[k].pipe[1]);
}
}
#if 0
/* setvbuf(stdout, NULL, _IONBF, 0); */
/* setbuf(stdout, NULL); */
for (k = 0; k < N_FD; k++) {
if (k > 0) {
fchmod(k, 030620);
fcntl(k, F_SETFL, O_NONBLOCK|O_NDELAY);
}
}
#endif
if (ptbl[i]->nice != 0
&& setpriority(PRIO_PROCESS, PRIO_PROCESS,
ptbl[i]->nice) == -1
&& ! cfg.quiet)
/* this is probably written to wrong place;
fd 2 (stderr) must not be real stderr */
msg_warn_print("cannot set priority %d for process %d\n",
ptbl[i]->nice, i);
execvp(ptbl[i]->argv[0], ptbl[i]->argv);
/*
* RETURN VALUES
* If any of the exec functions returns, an error will have
* occurred. The return value is -1, and the global variable
* errno will be set to indicate the error.
*/
/* msg_error_print("can't exec: %s\n", strerror(errno)); */
close(1);
close(2);
close(0);
/* execvp() fails, exiting */
exit(errno == 0 ? 255 : (errno < 0 ? -errno : errno));
}
}
/*
* All fork()-s were OK.
* Returning successfully.
*/
return 0;
}
void
proctable_close_2nd_sides_of_pipes(ptbl)
PROCTABLE ptbl;
{
register int i;
for (i = 1; ptbl[i] != NULL; i++)
process_close_2nd_sides_of_pipes(ptbl[i]);
}
int
proctable_remove_process(p_ptbl, proc_id)
PROCTABLE *p_ptbl;
const int proc_id;
{
register int i, j, k;
/*
* Pri odstraneni procesu je nutne:
* - odstranit process z tabulky PROCTABLE a posunut vsetky za nim
* - prepisat vsetky linky v PROCTABLE
* - zrusit vsetky linky odkazujuce na dany process
*
* Nepto [3/4/2001]
*/
#if DEBUG
msg_debug_print("proctable_remove_process(%p [%p], %d)\n",
p_ptbl, *p_ptbl, proc_id);
#endif
for (k = 0; k < N_FD; k++) {
if (proctable_remove_process_fd_links(*p_ptbl, proc_id, k) == -1)
return -1;
}
for (i = 0; (*p_ptbl)[i] != NULL; i++) {
for (k = 0; k < N_FD; k++) {
#if DEBUG
msg_debug_print(" process %d fd %d n_link %d\n",
i, k, (*p_ptbl)[i]->fd[k].n_link);
#endif
for (j = 0; j < (*p_ptbl)[i]->fd[k].n_link; /* empty */) {
if ((*p_ptbl)[i]->fd[k].link[j].proc_id == proc_id) {
/* This removing stuff was moved to begin of function. */
/* fd_remove_proclink_by_index(&(*p_ptbl)[i]->fd[k], j); */
}
else {
if ((*p_ptbl)[i]->fd[k].link[j].proc_id > proc_id)
(*p_ptbl)[i]->fd[k].link[j].proc_id--;
j++;
}
}
}
}
process_destroy((*p_ptbl)[proc_id]);
for (i = proc_id; (*p_ptbl)[i] != NULL; i++) {
(*p_ptbl)[i] = (*p_ptbl)[i + 1];
}
/* Process table is a list of process ended with NULL. */
/* Resizing process table: '-1' is for size down by 1 */
/* '+1' cause we already go down by 1 */
/* see the for () above */
/* '+1' is for ending NULL */
/* So the final is '+1'. */
*p_ptbl = (PROCTABLE) realloc(*p_ptbl,
(proctable_get_size(*p_ptbl) + 1) * sizeof(struct process *));
if (*p_ptbl == NULL) {
msg_error_print("proctable_remove_process(): realloc() failure\n");
return -1;
}
#if DEBUG
msg_debug_print("proctable_remove_process(%p [%p])\n",
p_ptbl, *p_ptbl);
#endif
return 0;
}
int
proctable_remove_process_fd_links(ptbl, proc_id, fd_id)
PROCTABLE ptbl;
int proc_id;
int fd_id;
{
while (ptbl[proc_id]->fd[fd_id].n_link > 0) {
register int a_proc_id, a_fd_id;
/* 0 means the first proclink;
we want remove them all continuosly */
a_proc_id = ptbl[proc_id]->fd[fd_id].link[0].proc_id;
a_fd_id = ptbl[proc_id]->fd[fd_id].link[0].fd_id;
/* Process 1: id: i, fd: k */
/* Process 2: id: proc_id, fd: fd_id */
if (
fd_remove_proclink2(
&((ptbl[a_proc_id])->fd[a_fd_id]),
proc_id, fd_id) == 0
||
fd_remove_proclink2(
&((ptbl[proc_id])->fd[fd_id]),
a_proc_id, a_fd_id) == 0
)
return -1;
}
return 0;
}
void
proctable_print(ptbl)
PROCTABLE ptbl;
{
register int i, j, k;
for (i = 0; ptbl[i] != NULL; i++) {
msg_print("Process %d (", i);
process_print_names(ptbl[i]);
msg_print("): ");
process_print_cmdline(ptbl[i]);
msg_print("\n");
for (k = 0; k < N_FD; k++) {
msg_print(" %s = (%d, %d)\n", fd_id_to_fd_str(k),
ptbl[i]->fd[k].pipe[0], ptbl[i]->fd[k].pipe[1]);
for (j = 0; j < ptbl[i]->fd[k].n_link; j++)
msg_print(" [%d].%s %s [%d].%s\n",
i, fd_id_to_fd_str(k),
((i&&k) || (!i&&!k)) ? "->" : "<-",
ptbl[i]->fd[k].link[j].proc_id,
fd_id_to_fd_str(ptbl[i]->fd[k].link[j].fd_id));
}
}
}
int
proctable_read(p_ptbl, p_rfds, p_wfds, p_efds)
PROCTABLE *p_ptbl;
fd_set *p_rfds;
fd_set *p_wfds;
fd_set *p_efds;
{
register int i, k, datasize = 1;
for (i = 0; (*p_ptbl)[i] != NULL; i++) {
for (k = 0; k < N_FD; k++) {
if (FD_ISSET((*p_ptbl)[i]->fd[k].pipe[0], p_efds)) {
FD_CLR((*p_ptbl)[i]->fd[k].pipe[0], p_efds);
#if DEBUG
msg_debug_print("proctable_read(): exception:"
" process %d, fd %d, real fd %d\n",
i, k, (*p_ptbl)[i]->fd[k].pipe[0]);
#endif
}
}
for (k = (i ? 1 : 0); k < (i ? N_FD : 1); k++) {
if (FD_ISSET((*p_ptbl)[i]->fd[k].pipe[0], p_rfds)) {
register int j;
FD_CLR((*p_ptbl)[i]->fd[k].pipe[0], p_rfds);
datasize = read((*p_ptbl)[i]->fd[k].pipe[0],
buf, BUFSIZE);
#if DEBUG
msg_debug_print("proctable_read(): descriptor change:"
" process %d, fd %d, datasize %d\n",
i, k, datasize);
#endif
if (datasize == 0) {
/* TODO: multi reads from one fd doesn't work!!! */
close((*p_ptbl)[i]->fd[k].pipe[0]);
#if DEBUG
msg_debug_print("proctable_read(): close(%d)\n",
(*p_ptbl)[i]->fd[k].pipe[0]);
#endif
}
if (datasize < 0) {
#if DEBUG
msg_debug_print("proctable_read(): "
"read() failure, %d returned\n",
datasize);
#endif
msg_errno_print();
msg_print("Terminating...");
exit(1);
}
if (datasize == 0) {
/* Variable datasize is 0, filedescriptor is closed.
Now we are going to remove all proclinks from/to this
filedescriptor. */
proctable_remove_process_fd_links(*p_ptbl, i, k);
}
else {
/* Variable datasize is not 0. Data were recieved.
We are going to transfer datasize bytes to linked
filedescriptors. */
for (j = 0; j < (*p_ptbl)[i]->fd[k].n_link; j++) {
register int proc_id, fd_id;
register int fd_write;
proc_id = (*p_ptbl)[i]->fd[k].link[j].proc_id;
fd_id = (*p_ptbl)[i]->fd[k].link[j].fd_id;
fd_write = (*p_ptbl)[proc_id]->fd[fd_id].pipe[1];
#if 0 && DEBUG
msg_debug_print("proctable_read(): descriptor write:"
" process %d, fd %d, datasize %d\n",
proc_idx, fd_write, datasize);
#endif
switch (write(fd_write, buf, datasize)) {
case -1:
/* This is some special case. We are writting
to filedescriptor,which probably desn't exists
(ie. due to child exit). With signal handling
and appropriate testing after select() return
we should never get here. But if signals are
not used, this situation is quite common
ep: debug: proctable_read(): write() failed: Broken pipe
So that descriptor is not usable anymore.
We will remove its proclinks. */
#if DEBUG
msg_debug_print("proctable_read(): "
"write() failed: %s\n",
strerror(errno));
#endif
proctable_remove_process_fd_links(*p_ptbl, proc_id, fd_id);
break;
case 0: /* Similar situation as -1 above */
#if DEBUG
msg_debug_print("proctable_read(): "
"write() returns 0: %s\n",
strerror(errno));
#endif
proctable_remove_process_fd_links(*p_ptbl, proc_id, fd_id);
break;
default:
break;
}
}
}
} /* if (FD_ISSET()) */
else
datasize = 1;
if (! datasize)
break;
}
if (! datasize)
break;
}
/* Returns 0 if process was removed, 1 elsewhere. */
return datasize;
}
int
proctable_wait(p_ptbl)
PROCTABLE *p_ptbl;
{
int pid = 0;
int status;
#if DEBUG
msg_debug_print("proctable_wait(): f_wait_call == 1, calling wait()\n");
#endif
/*
* Parameters in waitpid():
* 1. Value -1 means wait for any child process
* 2. Constant WNOHANG means return immediately if no child has exited.
*/
pid = waitpid(-1, &status, WNOHANG);
switch (pid) {
case -1:
#if 0
msg_error_print("proctable_wait(): "
"waitpid() failure: %s\n", strerror(errno));
#endif
break;
case 0:
#if DEBUG
msg_warn_print("proctable_wait(): "
"waitpid() warning: no child available\n");
#endif
/* We will check if there is a process which should be
terminated. If it is, we will return 1 and than main
program will call us again after select() timeout. */
{
register int i, k, proc_n_link = 1;
for (i = 1; (*p_ptbl)[i] != NULL; i++) {
for (proc_n_link = 0, k = 0; k < N_FD; k++)
proc_n_link += (*p_ptbl)[i]->fd[k].n_link;
if (proc_n_link == 0) {
#if DEBUG
msg_debug_print("proctable_wait(): candidate index = %d\n", i);
#endif
/* Don't need to browse the process table more. */
/* THIS IS WRONG! */
break;
}
}
if (proc_n_link == 0)
pid = 1;
}
break;
default:
{
register int index;
index = proctable_get_process_index_by_pid(*p_ptbl, pid);
#if DEBUG
msg_debug_print("proctable_wait(): "
"pid = %d, index = %d\n",
pid, index);
msg_debug_print("proctable_wait(): "
"proctable_get_size() = %d\n",
proctable_get_size(*p_ptbl));
#endif
if (! cfg.quiet) {
msg_print("[%d] ", pid);
if (WIFEXITED(status)) {
msg_print("Exit");
if (WEXITSTATUS(status))
msg_print(" %d", WEXITSTATUS(status));
}
else if (WIFSIGNALED(status)) {
register int sig_n = WTERMSIG(status);
switch (sig_n) {
case SIGKILL:
msg_print("Killed");
break;
case SIGTERM:
msg_print("Terminated");
break;
case SIGSEGV:
msg_print("Segmentation fault");
break;
default:
msg_print("Received signal %d", sig_n);
break;
};
}
else {
msg_print("Aborted", pid);
}
msg_print("\t\t\t\t");
msg_array_print((*p_ptbl)[index]->argv);
msg_print("\n");
} /* if (! cfg.quiet) */
#if DEBUG
msg_debug_print("proctable_wait(): "
"removing process from process table\n");
#endif
proctable_remove_process(p_ptbl, index);
#if DEBUG
msg_debug_print("proctable_wait(): "
"proctable_get_size() = %d\n",
proctable_get_size(*p_ptbl));
#endif
}
break;
} /* switch (pid) */
return pid;
}
int
proctable_wait2(p_ptbl)
PROCTABLE *p_ptbl;
{
register int i, k, proc_n_link = 1;
int pid = 0;
int status;
for (i = 1; (*p_ptbl)[i] != NULL; i++) {
for (proc_n_link = 0, k = 0; k < N_FD; k++)
proc_n_link += (*p_ptbl)[i]->fd[k].n_link;
if (proc_n_link == 0) {
#if DEBUG
msg_debug_print("proctable_wait2(): candidate index = %d\n", i);
#endif
pid = waitpid((*p_ptbl)[i]->pid, &status, WNOHANG);
switch (pid) {
case -1:
#if 0
msg_error_print("proctable_wait2(): "
"waitpid() failure: %s\n", strerror(errno));
#endif
return pid;
break;
case 0:
#if DEBUG
msg_warn_print("proctable_wait2(): "
"waitpid() warning: no child available\n");
#endif
break;
default:
if (! cfg.quiet) {
msg_print("[%d] ", pid);
if (WIFEXITED(status)) {
msg_print("Exit");
if (WEXITSTATUS(status))
msg_print(" %d", WEXITSTATUS(status));
}
else if (WIFSIGNALED(status)) {
register int sig_n = WTERMSIG(status);
switch (sig_n) {
case SIGKILL:
msg_print("Killed");
break;
case SIGTERM:
msg_print("Terminated");
break;
case SIGSEGV:
msg_print("Segmentation fault");
break;
default:
msg_print("Received signal %d", sig_n);
break;
};
}
else {
msg_print("Aborted", pid);
}
msg_print("\t\t\t\t");
msg_array_print((*p_ptbl)[i]->argv);
msg_print("\n");
} /* if (! cfg.quiet) */
proctable_remove_process(p_ptbl, i);
return pid;
break;
}
}
}
return 0;
}
Platon Group <platon@platon.org> http://platon.org/
|