home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
OS/2 Shareware BBS: 5 Edit
/
05-Edit.zip
/
me34src.zip
/
me3
/
comserver
/
comserver.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-01-14
|
22KB
|
990 lines
/*
* Compute Server for ME2
*
* comserver socket-name
*/
/*
* Notes:
* select() sez a pipe has input when the pipe is closed.
*/
/* Protocol
* Send/recieve packets
* Packet format: <type><bytes>
* <type> is 4 byte int
* STRING <length><bytes>
* NUMBER <number>
* Types of Packets:
* To server:
* Connect
* Disconnect
* Dir-to-compute-in <dir>
* Command-to-exec <command> <arg1> <arg2> ...
* Just-do-it
* Exec current cp.
* Stop-process
* Terminate the current process, if it is exec'ed.
* Can be exec'ed again.
* Delete-process
* Stop and remove from process server the cp.
* Send-me-signals <my-pid>
* Create-process
* Creates a cp.
* Make-process-current <cpid>
* Make a cp the current one so can change it, exec it, etc.
* To client:
???? what process
* Output-stdout <text>
* Output-stderr <text>
* Process completed, exit status
*/
/* Copyright 1991 Craig Durland
* Distributed under the terms of the GNU General Public License.
* Distributed "as is", without warranties of any kind, but comments,
* suggestions and bug reports are welcome.
*/
static char what[] = "@(#)Compute Server 2/17/91 v1.2 8/93";
#ifdef __STDC__
#ifdef __hpux /* for ANSI C on HP-UX */
#define _HPUX_SOURCE
#endif /* __hpux */
#ifdef __apollo /* for ANSI C on Apollo BSD */
#define _BSD_SOURCE
#endif /* __apollo */
#ifdef _AIX /* for ANSI C on IBM AIX */
#define _ALL_SOURCE
#endif /* _AIX */
#endif /* __STDC__ */
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <const.h>
#include <os.h>
#if BSD_OS || POSIX_OS || AIX_OS
#include <sys/time.h>
#else /* SYSV_OS */
#include <time.h>
#endif
#include <signal.h>
#include "comserver.h"
/* ********************* ***************************** */
typedef struct Process
{
int id; /* 0...? !!!??? rollover?? use table[10]? */
int pid; /* process id, 0 if process not running */
int two_pipe; /* TRUE if use seprate stdout and stderr */
int fd_output[2]; /* 0 is stdout, 1 is stderr */
int self_destruct; /* TRUE if go away when done */
int go_mask;
char
*dir, /* directory to compute in */
*command; /* command to exec */
struct Process *next;
} Process;
typedef struct Client
{
int
id_seed, /* 1...? !!!??? rollover?? don't use 0, use table[10]? */
pid, /* clients pid */
sig, /* signal to use to kick client */
socket, /* socket used to talk to client */
blocked; /* true if haven't written all of packet */
CSPacket packet;
Process
*processes, /* list of processes */
*current_process;
struct Client *next;
} Client;
/* ******************************************************************** */
/* ********************************* ********************************* */
/* ******************************************************************** */
static int
get_dpart(), open_request_socket(), another_server_running(), get_packet(),
connect_to_client();
static void
flush_client(), flush_all_clients(), process_done(), close_process();
/* ******************************************************************** */
/* ************************ Select Mask Stuff ************************* */
/* ******************************************************************** */
#define CONNECT 0
#define CLIENT_SEND 1
#define PROCESS_SEND 2
#define PROCESS_SEND_ERR 3
#define MAX_FDS 32 /* max file descripters */
typedef struct
{
int
class,
live; /* if fd is for real */
Client *client;
Process *process;
} FD;
#define MASK(f) (1 << (f))
static int select_mask, biggest_fd;
static FD fd_table[MAX_FDS]; /* all fields set to 0 */
static void make_select_mask()
{
FD *pfd;
int n, fd;
select_mask = 0;
biggest_fd = 0;
for (n = MAX_FDS; n--; )
{
pfd = &fd_table[n];
if (pfd->live && !(pfd->client && pfd->client->blocked))
{
fd = n;
select_mask |= MASK(fd);
biggest_fd = imax(biggest_fd, fd);
}
}
biggest_fd++;
}
static int add_fd(fd, client, process, class)
int fd; Client *client; Process *process; int class;
{
FD *pfd;
if (fd >= MAX_FDS || fd == -1) return FALSE; /* full up */
if (!fd_table[fd].live)
{
pfd = &fd_table[fd];
pfd->live = TRUE;
pfd->client = client;
pfd->process = process;
pfd->class = class;
make_select_mask();
}
return TRUE;
}
static int remove_fd(fd) int fd;
{
FD *pfd;
if (fd < 0 || fd >= MAX_FDS) return FALSE;
pfd = &fd_table[fd];
if (!pfd->live) return FALSE;
pfd->live = FALSE;
close(fd);
make_select_mask();
return TRUE;
}
static int selecter;
static int wait_for_something()
{
int readfds, nfound;
struct timeval timeout;
selecter = 0;
timeout.tv_sec = 2;
timeout.tv_usec = 0;
while (TRUE)
{
readfds = select_mask;
if ((nfound = select(biggest_fd, &readfds, 0, 0, &timeout)) == -1)
perror("select failed");
else
if (nfound == 0) /* select timeout */
{
flush_all_clients(); /* in case any are blocked */
}
else
{
selecter = readfds;
return TRUE;
}
}
/* doesn't get here */
}
static FD *whats_waiting()
{
int n, bit;
for (n = biggest_fd; n--; )
{
bit = MASK(n);
if (bit & selecter)
{
selecter &= ~bit;
return &fd_table[n];
}
}
return NULL;
}
/* ********************* ***************************** */
extern char *malloc();
static void sig_die();
main(argc, argv) int argc; char **argv;
{
char *socket_name;
FD *foo;
if (argc < 2) { printf("comserver socket-name\n"); exit(1); }
socket_name = argv[1];
if (another_server_running(socket_name))
{
printf("Another server running!\n On socket: \"%s\"\n",socket_name);
exit(1);
}
signal(SIGTERM,sig_die);
signal(SIGHUP, sig_die);
signal(SIGINT, sig_die);
signal(SIGPIPE,SIG_IGN);
if (!open_request_socket(socket_name)) exit(1);
while (TRUE)
{
wait_for_something();
while (foo = whats_waiting())
{
Client *client = foo->client;
Process *process = foo->process;
switch(foo->class)
{
case CONNECT: /* client trying to connect */
connect_to_client();
break;
case CLIENT_SEND: /* client sending me data */
get_packet(client);
break;
case PROCESS_SEND: /* process sending me data */
transfer_data(client, process, process->fd_output[0],
CS_OUTPUT, 0x1);
break;
case PROCESS_SEND_ERR: /* process sending me data */
transfer_data(client, process, process->fd_output[1],
CS_OUTPUT_ERR, 0x2);
break;
}
}
}
/* Doesn't get here */
}
/* ******************************************************************** */
/* ***************************** Signals ****************************** */
/* ******************************************************************** */
#include <sys/wait.h>
static int command_exit_status;
/* Signal handler for sig child.
* If we get this signal, we know wait() will return immediately.
*/
static void sig_child(dummy) int dummy;
{
int s, pid;
pid = wait(&s);
command_exit_status = WEXITSTATUS(s);
#if 0
{
Process *process = the_client.current_process;
if (process) process_done(&the_client, process);
else printf("process already closed\n");
}
#endif
printf("sig_child: pid = %d, s = %x, %d\n",pid, s, command_exit_status);
}
static void close_all_clients();
static void sig_die()
{
close_all_clients();
printf("\nCompute Server stopped via signal\n");
exit(1);
}
/* ******************************************************************** */
/* *********************** Exec Compute Process *********************** */
/* ******************************************************************** */
static char *zargv[50], zbuf[10000];
#define STDIN 0
#define STDOUT 1
#define STDERR 2
static int do_command(client) Client *client;
{
int command_pid, two_pipe, fd_out, fd_err, pipe_stdout[2], pipe_stderr[2];
Process *process;
if (!(process = client->current_process))
{
printf("No current process!\n");
return FALSE;
}
if (!process->command)
{
printf("No command!\n");
return FALSE;
}
two_pipe = process->two_pipe;
/* !!!error check */
pipe(pipe_stdout); fd_out = pipe_stdout[0];
if (two_pipe) { pipe(pipe_stderr); fd_err = pipe_stderr[0]; }
#if 1
signal(SIGCHLD,sig_child);
#else
signal(SIGCHLD,SIG_IGN);
#endif
switch(command_pid = fork())
{
default: /* I'm the parent */
process->pid = command_pid;
process->fd_output[0] = fd_out;
add_fd(fd_out, client, process, PROCESS_SEND); /* !!!error check */
close(pipe_stdout[1]);
if (two_pipe)
{
process->fd_output[1] = fd_err;
add_fd(fd_err, client, process, PROCESS_SEND_ERR); /* !!!error check */
close(pipe_stderr[1]);
}
break;
case 0: /* I'm the child */
if (process->dir && chdir(process->dir))
{
printf("chdir didn't\n");
exit(1);
}
dup2(pipe_stdout[1], STDOUT); close(pipe_stdout[0]);
if (two_pipe)
{ dup2(pipe_stderr[1], STDERR); close(pipe_stderr[0]); }
else dup2(pipe_stdout[1], STDERR); /* send stderr to stdout */
exec_ize(process);
if (-1 == execvp(zargv[0], zargv))
{
perror("Child exec error: ");
exit(1);
}
/* Never gets here */
case -1: /* error */
perror("Trying to fork: ");
/* !!!?????????? */
return FALSE;
}
/* NOTREACHED */
}
#define BUF_SIZE 200
/* suck up childs output and ship it to client */
transfer_data(client, process, fd, op, flag)
Client *client;
Process *process;
int fd, op;
{
char buf[BUF_SIZE+1];
int n;
n = read(fd, buf, BUF_SIZE);
if (-1 == n) { perror("transfer_data read: "); }
else
if (0 == n) { process->go_mask |= flag; } /* EoF */
else /* real data */
{
buf[n] = '\0';
CSbuild_packet(&client->packet, op, "ns", process->id, buf);
flush_client(client);
}
if (process->go_mask == 0x3) process_done(client, process);
}
#include <ctype.h>
/* Convert command in to some thing I can exec().
* Expand file names.
* "foo" -> foo
* If see a " >*> ", the rest of the line is one arg.
* Only after the first arg.
*/
exec_ize(process) Process *process;
{
char *ptr, *qtr;
int n;
ptr = process->command;
get_dpart(&ptr,zbuf); zargv[0] = zbuf;
qtr = zbuf; qtr += strlen(qtr); *qtr++ = '\0';
n = 1;
while (get_dpart(&ptr,qtr))
{
if (0 == strcmp("<*>", qtr))
{
zargv[n++] = ptr; /* the rest of the line */
break;
}
zargv[n] = qtr;
qtr += strlen(qtr); *qtr++ = '\0';
n++;
}
zargv[n] = NULL;
#if 0
for (n = 0; zargv[n]; n++) printf("argv[%d]: \"%s\"\n", n, zargv[n]);
#endif
}
static int get_dpart(start,word) char **start, *word;
{
register char *ptr = *start;
int quote = FALSE;
if (*ptr == '\0') return FALSE;
while (TRUE)
{
switch (*word++ = *ptr++)
{
case '"':
if (quote)
{
word--;
goto done;
}
word--;
quote = TRUE;
break;
case ' ': /* !!! really want isspace() */
if (quote) break;
while (isspace(*ptr)) ptr++;
word--;
goto done;
case '\0':
/* if in quote: boo-boo */
ptr--; /* back up to '\0' */
goto done;
}
}
done:
*start = ptr;
*word = '\0';
return TRUE;
}
/* ******************************************************************** */
/* *************************** Client Stuff *************************** */
/* ******************************************************************** */
static Client *first_client = NULL;
Client *create_client(socket) int socket;
{
Client *client;
if (!(client = (Client *)malloc(sizeof(Client)))) return NULL;
client->id_seed = 1;
client->pid = client->sig = 0;
client->blocked = FALSE;
client->packet.size = 0; /* !!! need an init_packet(); */
client->socket = socket;
client->processes = client->current_process = NULL;
client->next = first_client;
first_client = client;
return client;
}
static void add_process_to_client(client, process)
Client *client; Process *process;
{
client->current_process = process;
process->next = client->processes;
client->processes = process;
}
static void remove_process_from_client(client, process)
Client *client; Process *process;
{
Process *ptr, *drag;
ptr = client->processes;
if (ptr == process)
{
client->processes = ptr->next;
}
else
{
for (; ptr; ptr = ptr->next)
{
if (ptr == process) break;
drag = ptr;
}
drag->next = process->next;
}
if (client->current_process == process) client->current_process = NULL;
}
/* Allocate an id that a process can use.
* Returns:
* 0 : No ids available
* n : Your new process id.
*/
static int alloc_pid(client) Client *client;
{
return client->id_seed++;
}
static void close_client(client, error_code) Client *client;
{
Process *process, *ptr;
/* if (!client) return;*/
/* !!! don't write if client croaked */
if (error_code != -1)
{
CSbuild_packet(&client->packet, CS_ERROR, "nn", 0, error_code);
flush_client(client);
}
remove_fd(client->socket);
for (process = client->processes; process; process = ptr)
{
ptr = process->next; /* free() nukes next ptr */
close_process(process);
}
if (first_client == client) first_client = client->next;
else
{
Client *ptr, *qtr;
for (ptr = qtr = first_client; ptr; ptr = ptr->next)
{
if (ptr == client) break;
qtr = ptr;
}
qtr->next = ptr->next;
}
free((char *)client);
}
static void close_all_clients()
{
Client *client;
while (client = first_client) close_client(client,CS_ERROR_SERVER_DIED);
}
static void client_blocked(client, blocked) Client *client;
{
if (client->blocked == blocked) return;
client->blocked = blocked;
make_select_mask();
}
/* Notes:
* Gotta be a little careful when closing a client because
* close_client() calls flush_client().
*/
static void flush_client(client) Client *client;
{
CSPacket *pak = &client->packet;
int pid;
if (0 == pak->size) return; /* got nothing something to write */
switch(CSwrite_packet(pak, client->socket))
{
case -1: /* error : nuke client */
close_client(client, -1);
return;
case 0: client_blocked(client, TRUE); break;
case 1: client_blocked(client, FALSE); break;
case 2: return; /* didn't do nothing */
}
/* !!! only do this if write something? */
pid = client->pid;
if (pid && (-1 == kill(pid,client->sig)))
{ perror("flush_client: Signal client"); /* !!!????????? */}
}
static void flush_all_clients()
{
Client *client;
for (client = first_client; client; client = client->next)
flush_client(client);
}
/* Try and send a message to all clients.
* WARNINGS
* This is mostly a crock. If a client is blocked, that means I can't
* use its packet so I can't send it a message. Not good if there
* is a lot going on or a client is slow. Need some buffering.
*/
static void send_client_msg(tag, msg) int tag; char *msg;
{
Client *client;
flush_all_clients(); /* try and make sure packets empty */
for (client = first_client; client; client = client->next)
if (!client->blocked)
CSbuild_packet(&client->packet, CS_CLIENT_MSG, "ns", tag, msg);
flush_all_clients(); /* send the packets */
}
/* ******************************************************************** */
/* *************************** Socket Code **************************** */
/* ******************************************************************** */
extern char *savestr();
static int request_socket;
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
static int open_request_socket(socket_name) char *socket_name;
{
int len;
struct sockaddr_un unsock;
if ((request_socket = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
{
perror("Creating Unix socket");
return FALSE;
}
/* bzero((char *) &unsock, sizeof (unsock)); /* */
unsock.sun_family = AF_UNIX;
strcpy(unsock.sun_path, socket_name);
#ifdef SUN_LEN /* BSD44SOCKETS, SUN_LEN defined in sys/un.h on AIX */
unsock.sun_len = strlen(unsock.sun_path);
len = SUN_LEN(&unsock);
#else
#ifdef SCM_RIGHTS /* 4.3bsd reno and later (e.g. 386bsd) */
/* The following is taken from Stevens, _Advanced Programming in the
* UNIX Environment_. -Thomas Gellekum
*/
len = sizeof(unsock.sun_len) + sizeof(unsock.sun_family) +
strlen(unsock.sun_path) + 1;
unsock.sun_len = len;
#else
len = strlen(unsock.sun_path) + 2;
#endif /* SCM_RIGHTS */
#endif /* SUN_LEN */
/*len = strlen(unsock.sun_path) + sizeof(sun_family); /* ???vanilla 4.3bsd */
unlink (unsock.sun_path);
if (bind(request_socket, (struct sockaddr *)&unsock, len))
{
perror("Binding Unix socket");
close(request_socket);
return FALSE;
}
if (listen(request_socket, 20))
{
perror("Unix Listening");
close(request_socket);
return FALSE;
}
add_fd(request_socket, (Client *)NULL, (Process *)NULL, CONNECT);
return TRUE;
}
typedef struct sockaddr_un SocketType;
/*
* Notes:
* I need to the write socket to nonblocking so my round robin scheme
* will work. I prefer O_NONBLOCK but O_NDELAY seems to work.
*/
static int connect_to_client()
{
Client *client;
int fd, addrlen;
SocketType new; /* info on new socket */
addrlen = sizeof(SocketType);
if ((fd = accept(request_socket, (void *)&new, &addrlen)) == -1)
{
perror("connect_to_client: accept: ");
return FALSE;
}
#ifdef O_NONBLOCK
fcntl(fd, F_SETFL, O_NONBLOCK);
#else
fcntl(fd, F_SETFL, O_NDELAY);
#endif
if (!(client = create_client(fd))) { close(fd); return FALSE; }
if (!add_fd(fd, client, (Process *)NULL, CLIENT_SEND))
{
close_client(client, -1);
return FALSE;
}
return TRUE;
}
static int another_server_running(socket_name) char *socket_name;
{
int fd;
if (-1 == (fd = CSopen_client_socket(socket_name, 10))) return FALSE;
close(fd); /* hope that other server knows how to handle this */
return TRUE;
}
/* ******************************************************************** */
/* **************************** Processes ***************************** */
/* ******************************************************************** */
static void create_process(client, use_stdout_and_stderr) Client *client;
{
Process *process;
int id;
if (!(process = (Process *)malloc(sizeof(Process))) ||
!(id = alloc_pid(client)))
{
CSbuild_packet(&client->packet, CS_PROCESS_ID, "n", -1);
flush_client(client);
/* !!!??? no signal */
return;
}
process->id = id;
process->self_destruct = TRUE;
process->dir = NULL;
process->command = NULL;
process->pid = 0;
process->fd_output[0] = -1;
process->fd_output[1] = -1;
process->two_pipe = use_stdout_and_stderr;
process->go_mask = (use_stdout_and_stderr ? 0 : 2);
add_process_to_client(client, process);
CSbuild_packet(&client->packet, CS_PROCESS_ID, "n", process->id);
flush_client(client);
/* !!!??? no signal */
}
static void close_process(process) Process *process;
{
int n;
/* !!!??? gotta wait for the sigchild??? */
/* could put these on a kill list, send int and wait for sigchilds */
if (process->pid)
n = kill(process->pid, SIGINT);
if (process->dir) free(process->dir);
if (process->command) free(process->command);
remove_fd(process->fd_output[0]);
remove_fd(process->fd_output[1]);
free((char *)process);
}
static void process_done(client, process) Client *client; Process *process;
{
int exit_status;
printf("Process done\n"); fflush(stdout);
exit_status = 0; /* !!!??? */
CSbuild_packet(&client->packet, CS_PROCESS_DONE,
"nn", process->id, exit_status);
flush_client(client);
process->pid = 0;
remove_fd(process->fd_output[0]); process->fd_output[0] = -1;
remove_fd(process->fd_output[1]); process->fd_output[1] = -1;
if (process->self_destruct)
{
remove_process_from_client(client, process);
close_process(process);
}
}
/* ******************************************************************** */
/* ***************************** Packets ****************************** */
/* ******************************************************************** */
static int get_packet(client) Client *client;
{
char type;
CSPacket packet;
int fd;
Process *process;
xPacket event;
fd = client->socket;
process = client->current_process;
if (!CSread_packet(&packet, fd, &event)) /* error */
{
printf("Bad data: Client closed\n");
close_client(client, -1);
return FALSE;
}
type = event.type;
printf("type = %d\n",type); fflush(stdout);
switch(type)
{
default: /* invalid type */
close_client(client, CS_ERROR_PROTOCOL);
return FALSE;
case CS_CREATE_PROCESS:
printf("create-process: %d\n",event.u.Process.two_pipe); fflush(stdout);
create_process(client, event.u.Process.two_pipe);
break;
case CS_DISCONNECT:
close_client(client, -1);
return FALSE;
case CS_DIR:
if (process)
{
process->dir = savestr(event.u.Directory.dir);
printf("DIRECTORY: %s\n",process->dir); fflush(stdout);
}
break;
case CS_DO_IT:
printf("DO IT\n"); fflush(stdout);
do_command(client);
break;
case CS_COMMAND: /* command */
if (process)
{
if (process->command) free(process->command);
process->command = savestr(event.u.Command.command); /* !!! error check? */
printf("COMMAND: %s\n",process->command); fflush(stdout);
}
break;
case CS_SIGNAL: /* pid signal# */
client->pid = event.u.Signal.pid;
client->sig = event.u.Signal.sig;
printf("Client pid = %d %d\n",client->pid, client->sig);
fflush(stdout);
break;
case CS_CLIENT_MSG:
printf("client message: %d, %s\n",
event.u.ClientMsg.tag, event.u.ClientMsg.msg);
fflush(stdout);
send_client_msg(event.u.ClientMsg.tag, event.u.ClientMsg.msg);
break;
}
return TRUE;
}