* USE OR PERFORMANCE OF THIS SOFTWARE.
*
* $Log: wcgi.c,v $
- * Revision 1.11 1996/01/08 08:42:19 adam
+ * Revision 1.17 1996/02/26 10:36:15 adam
+ * Better error handling when (re)spawn of the shell fails.
+ *
+ * Revision 1.16 1996/02/12 10:10:29 adam
+ * Resource/config system used by the gateway.
+ *
+ * Revision 1.15 1996/01/26 09:02:20 adam
+ * Open of client FIFO called with O_NDELAY when reconnecting to shell
+ * in order to prevent serious lock if previous shell died without
+ * unlinking client FIFO.
+ *
+ * Revision 1.14 1996/01/12 13:08:06 adam
+ * CGI script passes name of lock file to the shell. The server will not close
+ * the response FIFO until this file becomes unlocked. This method handles
+ * cancel operations much better.
+ *
+ * Revision 1.13 1996/01/12 10:05:17 adam
+ * If script name ends with ';' HTTP/GET/Expires will be defined.
+ * The cgi interface only reads final handshake if response from
+ * server (shell) was zero-terminated [If it isn't it probably died].
+ *
+ * Revision 1.12 1996/01/09 10:46:49 adam
+ * New defines: LOGDIR/EGWDIR/CGIDIR set in Makefile.
+ *
+ * Revision 1.11 1996/01/08 08:42:19 adam
* Handles method GET.
*
* Revision 1.10 1996/01/05 16:21:20 adam
the server, please reload the server's 'front page'."
#include <gw-db.h>
+#include <gw-res.h>
#include "wproto.h"
-#define CGIDIR "/usr/local/etc/httpd/cgi-bin"
-
static char *prog = "cgi";
static char serverp[256] = {'\0'};
+static char serverf[256] = {'\0'};
static GW_DB gw_db = NULL;
static void fatal(char *p)
if (gw_db)
gw_db_close (gw_db);
if (*serverp)
- unlink(serverp);
+ unlink (serverp);
+ if (*serverf)
+ unlink (serverf);
exit(0);
}
static int spawn (char *sprog, int id)
{
- int r;
+ int r, fd;
char path[256];
char envstr[80];
sprintf (envstr, "GWID=%d", id);
putenv (envstr);
- sprintf(path, "%s/%s", CGIDIR, sprog);
+ sprintf(path, "%s/%s", EGWDIR, sprog);
switch(r = fork())
{
case -1:
gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "fork");
- exit(1);
+ fatal ("Internal error in server");
case 0:
close (0);
close (1);
gw_log (GW_LOG_DEBUG, prog, "execl %s", path);
execl (path, sprog, 0);
gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "execl %s", path);
- exit(0);
+ fd = open (serverp, O_WRONLY);
+ if (fd >= 0)
+ {
+ write (fd, "FA", 2);
+ close (fd);
+ }
+ exit(1);
default:
return r;
}
*/
int main()
{
- char clientp[256], tmp[256], *path_info, *p, *operation, *t;
+ char clientp[256], *path_info, *p, *operation, *t;
char combuf[COMBUF];
+ const char *fifoDir;
+ GwRes cgiRes;
+ int serverf_fd = -1;
int linein = -1, lineout, data, gw_id;
- chdir ("/usr/local/etc/httpd/cgi-bin");
+ if (chdir (EGWDIR))
+ fatal ("Couldn't change directory to " EGWDIR);
gw_log_init ("egw");
- gw_log_file (GW_LOG_ALL, "/usr/local/etc/httpd/logs/egwcgi_log");
+ gw_log_file (GW_LOG_ALL, "egwcgi_log");
+#if 0
gw_log_level (GW_LOG_ALL);
+#endif
+ gw_log_session (getpid());
gw_log (GW_LOG_STAT, prog, "Europagate www cgi server");
+ cgiRes = gw_res_init ();
+ gw_res_merge (cgiRes, "egw.res");
- sprintf(tmp, "%s/%s", FIFOROOT, FIFODIR);
- if (access(tmp, R_OK|W_OK) < 0 && mkdir(tmp, 0777) < 0)
+#if 1
+ gw_log_level (gw_log_mask_str (
+ gw_res_get (cgiRes, "log.level", "default")));
+#endif
+ fifoDir = gw_res_get (cgiRes, "fifo.dir", "/tmp/egw");
+
+ /* Create fifo directory if it doesn't exist already */
+ if (access(fifoDir, R_OK|W_OK) < 0 && mkdir(fifoDir, 0777) < 0)
{
- gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "Failed to create %s", tmp);
+ gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "Failed to create %s",
+ fifoDir);
fatal("Internal error in server.");
}
- sprintf(serverp, "%s/srv%d", tmp, getpid());
+ /* Delete server FIFO if it does exist */
+ sprintf(serverp, "%s/srv%d", fifoDir, getpid());
if (access(serverp, R_OK|W_OK) == 0)
{
if (unlink(serverp) < 0)
else
gw_log (GW_LOG_WARN, prog, "Removed stale server fifo.");
}
+ /* Make server FIFO */
if (mkfifo(serverp, 0666 | S_IFIFO) < 0)
{
gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "mkfifo(%s)", serverp);
fatal("Internal error in server.");
}
+ /* The httpd server must pass PATH_INFO */
if (!(path_info = getenv("PATH_INFO")))
{
gw_log (GW_LOG_FATAL, prog, "Must set PATH_INFO.");
fatal("Internal error in server.");
}
+ /* Create lock file that ensures the server (shell) doesn't */
+ /* terminate before we have read the whole response */
+ sprintf (serverf, "%s/srf%d", fifoDir, getpid ());
+ gw_log (GW_LOG_DEBUG, prog, "open w %s", serverf);
+ serverf_fd = open (serverf, O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ if (serverf_fd == -1)
+ {
+ gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "open (%s)", serverf);
+ fatal("Internal error in server.");
+ }
+ else
+ {
+ struct flock area;
+ area.l_type = F_WRLCK;
+ area.l_whence = SEEK_SET;
+ area.l_start = 0L;
+ area.l_len = 0L;
+ fcntl (serverf_fd, F_SETLK, &area);
+ }
+ /* Read first part of path_info. Either it is a numeric session id */
+ /* or it is the name of a backend shell. */
operation = ++path_info;
while (*path_info && *path_info != '/')
path_info++;
if (*path_info)
*(path_info++) = '\0';
- if (!(gw_db = gw_db_open ("www.db", 1, 1)))
+ gw_log (GW_LOG_DEBUG, prog, "www.db open");
+ if (!(gw_db = gw_db_open (EGWDIR "/www.db", 1, 1)))
{
gw_log (GW_LOG_FATAL, prog, "gw_db_open");
exit (1);
}
+ gw_log (GW_LOG_DEBUG, prog, "www.db ok");
+ /* Is operation a backend shell (new session) ? */
if ((gw_id = atoi(operation)) <= 0)
{
int r;
char gw_id_str[16];
+ /* Get new unique id */
gw_id = gw_db_seq_no (gw_db);
sprintf (gw_id_str, "%d", gw_id);
-
+
+ /* Spawn backend shell (server) */
spawn(operation, gw_id);
r = gw_db_insert (gw_db, gw_id_str, strlen(gw_id_str)+1,
operation, strlen(operation)+1);
gw_log (GW_LOG_DEBUG, prog, "Synchronizing with client");
if ((linein = open(serverp, O_RDONLY)) < 0)
{
- gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "open %s", serverp);
+ gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "open r %s", serverp);
fatal("Internal error in server.");
}
if (read(linein, combuf, 2) < 2 || strcmp(combuf, "OK"))
fatal("Internal error in server");
}
gw_log (GW_LOG_DEBUG, prog, "Synchronized");
+ sprintf(clientp, "%s/clt%d", fifoDir, gw_id);
+ gw_log (GW_LOG_DEBUG, prog, "open w %s", clientp);
+ lineout = open (clientp, O_WRONLY);
}
- sprintf(clientp, "%s/clt%d", tmp, gw_id);
- gw_log (GW_LOG_DEBUG, prog, "Opening %s", clientp);
- if ((lineout = open(clientp, O_WRONLY)) < 0)
+ else /* A session is continued */
+ {
+ sprintf(clientp, "%s/clt%d", fifoDir, gw_id);
+ gw_log (GW_LOG_DEBUG, prog, "open w|n %s", clientp);
+ /* Open the FIFO in O_NDELAY-mode: This prevents blocking */
+ /* even though the shell died without unlinking the FIFO */
+ /* On the other hand, if the shell is running, it will never */
+ /* close this FIFO */
+ lineout = open (clientp, O_WRONLY|O_NDELAY);
+ }
+ /* If open of clientp failed, the shell is not running, so we */
+ /* invoke it again */
+ if (lineout < 0)
{
char gw_id_str[16];
void *sprog;
gw_log (GW_LOG_FATAL, prog, "gw_db_lookup %s", gw_id_str);
fatal("Internal error in server");
}
- gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, prog, "open %s restart", clientp);
+ gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, prog, "open r %s restart", clientp);
spawn (sprog, gw_id);
gw_log (GW_LOG_DEBUG, prog, "Synchronizing with client");
if ((linein = open(serverp, O_RDONLY)) < 0)
gw_db_close (gw_db);
gw_log (GW_LOG_DEBUG, prog, "Decoding user data");
p = combuf + sizeof(data);
- strcpy(p, serverp);
- p += strlen(p) + 1;
- strcpy(p, path_info);
- gw_log (GW_LOG_DEBUG, prog, "P:%s", p);
+ strcpy (p, serverp);
+ p += strlen (p) + 1;
+ strcpy (p, serverf);
+ p += strlen (p) + 1;
+ strcpy (p, path_info);
+ gw_log (GW_LOG_STAT, prog, "P:%s", p);
p += strlen(p) + 1;
*(p++) = '\0'; /* no envvars tranferred at present */
if ((t = getenv("CONTENT_LENGTH")) && (data = atoi(t)) > 0)
}
if (linein < 0)
{
- gw_log (GW_LOG_DEBUG, prog, "open %s", serverp);
+ gw_log (GW_LOG_DEBUG, prog, "open r %s", serverp);
if ((linein = open(serverp, O_RDONLY)) < 0)
{
gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "open %s", serverp);
}
gw_log (GW_LOG_DEBUG, prog, "Reading response");
-#if 1
while ((data = read(linein, combuf, COMBUF)) > 0)
{
gw_log (GW_LOG_DEBUG, prog, "Got %d bytes", data);
}
if (data > 0)
{
- --data;
- if (write(1, combuf, data) < data)
- {
- gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "write");
- exit (1);
- }
- }
-#else
-# if 1
- fcntl (linein, F_SETFL, O_NONBLOCK);
-# endif
- while (1)
- {
- fd_set s_input;
- struct timeval t;
- int r, eof_flag = 0;
-
- t.tv_sec = 10;
- t.tv_usec = 0;
- FD_ZERO(&s_input);
- FD_SET(linein, &s_input);
-# if 0
- FD_SET(1, &s_input);
-# endif
- gw_log (GW_LOG_DEBUG, prog, "select");
- r = select (linein + 1, &s_input, NULL, NULL, &t);
- if (r < 0)
+ if (close (serverf_fd))
{
- gw_log (GW_LOG_ERRNO|GW_LOG_FATAL, prog, "select");
- exit(1);
+ gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "close %s", serverf);
}
- if (r == 0 || FD_ISSET (linein, &s_input))
+ if (--data > 0)
{
- if (r == 0)
- gw_log (GW_LOG_DEBUG, prog, "poll");
- if ((data = read (linein, combuf, COMBUF)) > 0)
+ if (write(1, combuf, data) < data)
{
- if (combuf[data-1] == '\0')
- {
- --data;
- eof_flag = 1;
- }
- gw_log (GW_LOG_DEBUG, prog, "Got %d bytes", data);
- if (data > 0 && write(1, combuf, data) < data)
- {
- gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "write");
- exit (1);
- }
+ gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "write");
+ exit (1);
}
- else if (data == -1)
- {
- if (r > 0)
- {
- gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "read");
- exit (1);
- }
- gw_log (GW_LOG_DEBUG, prog, "poll read");
- }
- else
- break;
- }
- if (eof_flag)
- break;
- if (r > 0 && FD_ISSET (1, &s_input))
- {
- data = read (1, combuf, COMBUF);
- if (data == -1)
- {
- gw_log (GW_LOG_DEBUG|GW_LOG_ERRNO, prog, "stdout closed");
- break;
- }
- if (data == 0)
- {
- gw_log (GW_LOG_DEBUG, prog, "stdout closed");
- break;
- }
- gw_log (GW_LOG_DEBUG, prog, "stdout got %d bytes", data);
}
}
-#endif
-
-#if 1
- gw_log (GW_LOG_DEBUG, prog, "writing ack");
- if (write(lineout, "OK", 2) < 2)
- gw_log (GW_LOG_FATAL|GW_LOG_ERRNO, prog, "write");
-#endif
gw_log (GW_LOG_DEBUG, prog, "Cleaning up.");
+ unlink (serverf);
close(linein);
unlink(serverp);
close(lineout);