]> code.citadel.org Git - citadel.git/commitdiff
Got the CitadelAPI library to the point where the server can start
authorArt Cancro <ajc@citadel.org>
Thu, 6 Aug 1998 23:27:02 +0000 (23:27 +0000)
committerArt Cancro <ajc@citadel.org>
Thu, 6 Aug 1998 23:27:02 +0000 (23:27 +0000)
up an extension, and the extension will connect to the server, do
some initialization, call a user-supplied CtdlMain(), and exit.  Also
hacked together a _temporary_ form of the new EXTN server command.

14 files changed:
citadel/ChangeLog
citadel/Makefile.in
citadel/citadel.c
citadel/citadel.h
citadel/citadelapi.c [new file with mode: 0644]
citadel/citserver.c
citadel/commands.c
citadel/ipc_c_tcp.c
citadel/ipcdef.h
citadel/messages.c
citadel/routines.c
citadel/serv_info.c [new file with mode: 0644]
citadel/serverapi.c [deleted file]
citadel/techdoc/api.txt

index 2b23cbd4aa520528daba16af055e75bbcbfa8e17..e4186819f250c29cd99e9b9a0bf1abb477ce27f0 100644 (file)
@@ -1,3 +1,9 @@
+Thu Aug  6 19:25:01 EDT 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
+       * Got the CitadelAPI library to the point where the server can start
+         up an extension, and the extension will connect to the server, do
+         some initialization, call a user-supplied CtdlMain(), and exit.  Also
+         hacked together a _temporary_ form of the new EXTN server command.
 Wed Aug  5 23:02:22 EDT 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
        * Second attempt at getting the server API started.  Now it runs
          outside of the server and builds a connection.
index 2f718332d152783ed7a89665f2142f3612a7be17..d58f8e2b554f126d7c821da7fa74768c885c49a1 100644 (file)
@@ -12,7 +12,7 @@
 
 client: citadel whobbs
 
-server: citserver setup
+server: citserver setup citadelapi.a
 
 utils: aidepost netmailer netproc netsetup msgform \
 readlog rcit stats sysoputil citmail netpoll mailinglist userlist
@@ -21,9 +21,9 @@ readlog rcit stats sysoputil citmail netpoll mailinglist userlist
 #
 
 citadel: ipc_c_tcp.o citadel.o rooms.o routines.o routines2.o messages.o \
-       commands.o client_chat.o
+       commands.o client_chat.o serv_info.o
        $(CC) $(CFLAGS) ipc_c_tcp.o citadel.o rooms.o routines.o routines2.o \
-       messages.o commands.o client_chat.o $(LFLAGS) -o citadel
+       messages.o commands.o client_chat.o serv_info.o $(LFLAGS) -o citadel
 
 netpoll: netpoll.c config.o ipc_c_tcp.o
        $(CC) $(CFLAGS) netpoll.c config.o ipc_c_tcp.o $(LFLAGS) -o netpoll
@@ -55,6 +55,9 @@ routines2.o: routines2.c citadel.h
 client_chat.o: client_chat.c citadel.h
        $(CC) -O $(CFLAGS) -c client_chat.c
 
+serv_info.o: serv_info.c citadel.h
+       $(CC) -O $(CFLAGS) -c serv_info.c
+
 
 #
 #
@@ -112,6 +115,13 @@ config.o: config.c config_decls.h citadel.h axdefs.h
 sysdep.o: sysdep.c citadel.h
        $(CC) -O $(CFLAGS) -D_REENTRANT -c sysdep.c
 
+citadelapi.a: citadelapi.o ipc_c_tcp.o serv_info.o
+       $(AR) r citadelapi.a citadelapi.o ipc_c_tcp.o serv_info.o
+       $(RANLIB) citadelapi.a
+
+citadelapi.o: citadelapi.c citadel.h
+       $(CC) -O $(CFLAGS) -D_REENTRANT -c citadelapi.c
+
 aidepost: aidepost.c config.o citadel.h
        $(CC) -O $(CFLAGS) aidepost.c config.o $(LFLAGS) -o aidepost
 
index c50be4f5f508a51c4509b2e5e5eaa53790c1e5cd..80a63c09e27d6084fab5a4ed28eaa82484fbca86 100644 (file)
@@ -112,7 +112,7 @@ char printcmd[256];                 /* print command */
 int editor_pid = (-1);
 char fullname[32];
 jmp_buf nextbuf;
-struct serv_info serv_info;            /* Info on the server connected */
+struct CtdlServInfo serv_info;         /* Info on the server connected */
 int screenwidth;
 int screenheight;
 unsigned room_flags;
@@ -263,55 +263,6 @@ void userlist() {
        }
 
 
-/*
- * get info about the server we've connected to
- */
-void get_serv_info() {
-       char buf[256];
-       int a;
-
-       /* fetch info */        
-       serv_puts("INFO");
-       serv_gets(buf);
-       if (buf[0]!='1') return;
-
-       a = 0;
-       while(serv_gets(buf), strcmp(buf,"000")) {
-           switch(a) {
-               case 0:         serv_info.serv_pid = atoi(buf);
-                               break;
-               case 1:         strcpy(serv_info.serv_nodename,buf);
-                               break;
-               case 2:         strcpy(serv_info.serv_humannode,buf);
-                               break;
-               case 3:         strcpy(serv_info.serv_fqdn,buf);
-                               break;
-               case 4:         strcpy(serv_info.serv_software,buf);
-                               break;
-               case 5:         serv_info.serv_rev_level = atoi(buf);
-                               break;
-               case 6:         strcpy(serv_info.serv_bbs_city,buf);
-                               break;
-               case 7:         strcpy(serv_info.serv_sysadm,buf);
-                               break;
-               case 9:         strcpy(serv_info.serv_moreprompt,buf);
-                               break;
-               case 10:        serv_info.serv_ok_floors = atoi(buf);
-                               break;
-               }
-           ++a;
-           }
-
-       /* be nice and identify ourself to the server */
-       sprintf(buf,"IDEN %d|%d|%d|%s|",
-               SERVER_TYPE,0,REV_LEVEL,
-               (server_is_local ? "(local)" : CITADEL));
-       locate_host(&buf[strlen(buf)]); /* append to the end */
-       serv_puts(buf);
-       serv_gets(buf); /* we don't care about the result code */
-
-       }
-
 /*
  * grab assorted info about the user...
  */
@@ -809,6 +760,28 @@ int set_password() {
        }
 
 
+
+/*
+ * get info about the server we've connected to
+ */
+void get_serv_info() {
+       char buf[256];
+
+       CtdlInternalGetServInfo(&serv_info);
+
+       /* be nice and identify ourself to the server */
+       sprintf(buf,"IDEN %d|%d|%d|%s|",
+               SERVER_TYPE,0,REV_LEVEL,
+               (server_is_local ? "local" : CITADEL));
+       locate_host(&buf[strlen(buf)]); /* append to the end */
+       serv_puts(buf);
+       serv_gets(buf); /* we don't care about the result code */
+       }
+
+
+
+
+
 /*
  * Display list of users currently logged on to the server
  */
index df6cd97a9cd6eab93e238491c4458391b7fc90c2..1ac342999929bdc12050aa871bda2d28d6bec406 100644 (file)
@@ -7,7 +7,7 @@
 #include "sysdep.h"
 #include "sysconfig.h"
 #include "ipcdef.h"
-#define CITADEL        "Citadel/UX DR5.03"
+#define CITADEL        "Citadel/UX DR19980806"
 #define REV_LEVEL 503
 #define SERVER_TYPE 0  /* zero for stock Citadel/UX; other developers please
                           obtain SERVER_TYPE codes for your implementations */
diff --git a/citadel/citadelapi.c b/citadel/citadelapi.c
new file mode 100644 (file)
index 0000000..ffdc6ca
--- /dev/null
@@ -0,0 +1,112 @@
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include "citadel.h"
+
+struct CtdlServerHandle CtdlAppHandle;
+struct CtdlServInfo CtdlAppServInfo;
+void CtdlMain();
+
+void logoff(exitcode) {
+       exit(exitcode);
+       }
+
+/*
+ * Programs linked against the Citadel server extension library need to
+ * be called with the following arguments:
+ * 0 - program name (as always)
+ * 1 - server address (usually 127.0.0.1)
+ * 2 - server port number
+ * 3 - internal program secret
+ * 4 - user name
+ * 5 - user password
+ * 6 - initial room
+ * 7 - associated client session
+ * 
+ */
+
+main(argc, argv)
+int argc;
+char *argv[]; {
+       int a;
+       char buf[256];
+
+       /* We're really not interested in stdio */
+       close(0);
+       close(1);
+       close(2);
+
+       /* Bail out if someone tries to run this thing manually */
+       if (argc < 3) exit(1);
+
+       /* Zeroing out the server handle neatly sets the values of
+        * CtdlAppHandle to sane default values
+        */
+       bzero(&CtdlAppHandle, sizeof(struct CtdlServerHandle));
+
+       /* Now parse the command-line arguments fed to us by the server */
+       for (a=0; a<argc; ++a) switch(a) {
+               case 1: strcpy(CtdlAppHandle.ServerAddress, argv[a]);
+                       break;
+               case 2: CtdlAppHandle.ServerPort = atoi(argv[a]);
+                       break;
+               case 3: strcpy(CtdlAppHandle.ipgmSecret, argv[a]);
+                       break;
+               case 4: strcpy(CtdlAppHandle.UserName, argv[a]);
+                       break;
+               case 5: strcpy(CtdlAppHandle.Password, argv[a]);
+                       break;
+               case 6: strcpy(CtdlAppHandle.InitialRoom, argv[a]);
+                       break;
+               case 7: CtdlAppHandle.AssocClientSession = atoi(argv[a]);
+                       break;
+               }
+
+       /* Connect to the server */
+       argc = 3;
+       attach_to_server(argc, argv);
+       serv_gets(buf);
+       if (buf[0] != '2') exit(1);
+
+       /* Set up the server environment to our liking */
+
+       CtdlInternalGetServInfo(&CtdlAppServInfo, 0);
+
+       sprintf(buf, "IDEN 0|5|006|CitadelAPI Client");
+       serv_puts(buf);
+       serv_gets(buf);
+
+       if (strlen(CtdlAppHandle.ipgmSecret) > 0) {
+               sprintf(buf, "IPGM %s", CtdlAppHandle.ipgmSecret);
+               serv_puts(buf);
+               serv_gets(buf);
+               }
+
+       if (strlen(CtdlAppHandle.UserName) > 0) {
+               sprintf(buf, "USER %s", CtdlAppHandle.UserName);
+               serv_puts(buf);
+               serv_gets(buf);
+               sprintf(buf, "PASS %s", CtdlAppHandle.Password);
+               serv_puts(buf);
+               serv_gets(buf);
+               }
+
+       sprintf(buf, "GOTO %s", CtdlAppHandle.InitialRoom);
+       serv_puts(buf);
+       serv_gets(buf);
+       if (buf[0] != '2') {
+               serv_puts("GOTO _BASEROOM_");
+               serv_gets(buf);
+               }
+
+       /* Now do the loop. */
+       CtdlMain();
+
+       /* Clean up gracefully and exit. */
+       serv_puts("QUIT");
+       exit(0);
+       }
index 51ddd9a180d8aeddebf7d0d46fcd22bc7c1d6ef8..afa6ae97839cbae2da824a3cc5dae5ddc7323ba4 100644 (file)
@@ -567,7 +567,7 @@ void cmd_down(void) {
        }
 
 /*
- * Shut down the server
+ * Schedule or cancel a server shutdown
  */
 void cmd_scdn(char *argbuf)
 {
@@ -591,6 +591,62 @@ void cmd_scdn(char *argbuf)
        cprintf("%d %d\n", OK, ScheduledShutdown);
        }
 
+
+/*
+ * Run a server extension (FIX FIX FIX initial hack; polish this up)
+ */
+void cmd_extn(char *argbuf) {
+       char ExtensionName[256];
+       int is_ipgm;
+       int pid;
+       char portstr[16];
+       char ipgm[32];
+       char sess[16];
+
+
+       extract(ExtensionName, argbuf, 0);
+       is_ipgm = extract_int(argbuf, 1);
+       
+       pid = fork();
+       lprintf(9, "fork() returned %d\n", pid);
+       if (pid < 0) {
+               cprintf("%d fork failed: %s\n",
+                       ERROR + INTERNAL_ERROR,
+                       strerror(errno));
+               return;
+               }
+       else if (pid == 0) {
+
+               sprintf(portstr, "%d", config.c_port_number);
+
+               if (is_ipgm)
+                       sprintf(ipgm, "%d", config.c_ipgm_secret);
+               else
+                       strcpy(ipgm, "");
+
+               sprintf(sess, "%d", CC->cs_pid);
+
+               execlp(ExtensionName, ExtensionName,
+                       "localhost",                    /* server address */
+                       portstr,                        /* port number */
+                       ipgm,                           /* ipgm secret */
+                       CC->usersupp.fullname,          /* user name */
+                       CC->usersupp.password,          /* password */
+                       CC->quickroom.QRname,           /* current room */
+                       sess,                           /* assoc session id */
+                       NULL);
+       
+               lprintf(9, "exec() failed: %s\n", strerror(errno));
+               exit(1);
+               }
+       else {
+               cprintf("%d Ok\n", OK);
+               }
+       }
+
+
+
+
 /*
  * main context loop
  */
@@ -1009,6 +1065,9 @@ void *context_loop(struct CitContext *con)
                else if (!strncasecmp(cmdbuf, "RCHG", 4)) {
                        cmd_rchg(&cmdbuf[5]);
                        }
+               else if (!strncasecmp(cmdbuf, "EXTN", 4)) {
+                       cmd_extn(&cmdbuf[5]);
+                       }
                else {
                        cprintf("%d Unrecognized or unsupported command.\n",
                                ERROR);
index 77cb8308d4685643c51ed8c679f450baa1a7b344..55a90dcfd52403c743b079676956f97975924fbd 100644 (file)
@@ -44,7 +44,7 @@ struct citcmd {
 
 extern unsigned room_flags;
 extern char room_name[];
-extern struct serv_info serv_info;
+extern struct CtdlServInfo serv_info;
 extern char axlevel;
 extern char is_room_aide;
 extern unsigned userflags;
index 2b8550f25406c97cbce549566febbe84c83adb3e..df0844455e3f9e14363ae6c33f2d99b3846eb038 100644 (file)
@@ -47,12 +47,12 @@ int serv_sock;
 
 u_long inet_addr();
 
-void timeout() {
+void CtdlInternalTCPtimeout() {
        printf("\rConnection timed out.\n");
        logoff(3);
        }
 
-int connectsock(host,service,protocol)
+int CtdlInternalTCPConnectSock(host,service,protocol)
 char *host;
 char *service;
 char *protocol; {
@@ -105,7 +105,7 @@ char *protocol; {
                }
 
 
-       signal(SIGALRM,timeout);
+       signal(SIGALRM,CtdlInternalTCPtimeout);
        alarm(30);
 
        if (connect(s,(struct sockaddr *)&sin,sizeof(sin))<0) {
@@ -124,7 +124,7 @@ char *protocol; {
  * convert service and host entries into a six-byte numeric in the format
  * expected by a SOCKS v4 server
  */
-void numericize(buf,host,service,protocol)
+void CtdlInternalTCPnumericize(buf,host,service,protocol)
 unsigned char buf[];
 char *host;
 char *service;
@@ -282,12 +282,12 @@ char *argv[]; {
 
        /* if not using a SOCKS proxy server, make the connection directly */
        if (strlen(socks4)==0) {
-               serv_sock = connectsock(cithost,citport,"tcp");
+               serv_sock = CtdlInternalTCPConnectSock(cithost,citport,"tcp");
                return;
                }
 
        /* if using SOCKS, connect first to the proxy... */
-       serv_sock = connectsock(socks4,"1080","tcp");
+       serv_sock = CtdlInternalTCPConnectSock(socks4,"1080","tcp");
        printf("Connected to SOCKS proxy at %s.\n",socks4);
        printf("Attaching to server...\r");
        fflush(stdout);
@@ -297,7 +297,7 @@ char *argv[]; {
                1);                     /* method = connect */
        serv_write(buf,2);
 
-       numericize(buf,cithost,citport,"tcp");
+       CtdlInternalTCPnumericize(buf,cithost,citport,"tcp");
        serv_write(buf,6);              /* port and address */
 
        p = (struct passwd *) getpwuid(getuid());
index 68d9db02a1a11ac25ab4a88ef7be43af8aa1da43..a953ffadf299a47e7be1c5ec2c27d4864f4b66d4 100644 (file)
@@ -25,7 +25,7 @@
 #define NO_SUCH_SYSTEM         73
 #define ALREADY_EXISTS         74
 
-struct serv_info {
+struct CtdlServInfo {
        int serv_pid;
        char serv_nodename[32];
        char serv_humannode[64];
@@ -70,3 +70,14 @@ struct serv_info {
 
 void serv_puts();
 void serv_gets();
+
+struct CtdlServerHandle {
+       char ServerAddress[64];
+       int ServerPort;
+       char ipgmSecret[32];
+       char UserName[32];
+       char Password[32];
+       char InitialRoom[32];
+       int AssocClientSession;
+       };
+
index b3a9cf16a1338b34459a0f261f425280f0bcda4c..22d2c49530eec481a104af9b3e5003433f6f32f6 100644 (file)
@@ -46,7 +46,7 @@ int num_msgs;
 extern char room_name[];
 extern unsigned room_flags;
 extern long highest_msg_read;
-extern struct serv_info serv_info;
+extern struct CtdlServInfo serv_info;
 extern char temp[];
 extern char temp2[];
 extern int screenwidth;
index 2d47c6b6d30299de52fdfcd0bd255f4a4ff7aea3..d7a2cf0f234af54b39682d8de835011adc8fa6c8 100644 (file)
@@ -37,7 +37,7 @@ void color();
 extern unsigned userflags;
 extern char *axdefs[7];
 extern char sigcaught;
-extern struct serv_info serv_info;
+extern struct CtdlServInfo serv_info;
 extern char rc_floor_mode;
 
 int struncmp(lstr,rstr,len)
diff --git a/citadel/serv_info.c b/citadel/serv_info.c
new file mode 100644 (file)
index 0000000..6787964
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * serv_info.c
+ *
+ * The CtdlGetServerInfo() function is useful for 
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <time.h>
+#include <ctype.h>
+#include <string.h>
+#include <errno.h>
+#include "citadel.h"
+
+void CtdlInternalGetServInfo(struct CtdlServInfo *infobuf) {
+       char buf[256];
+       int a;
+
+       /* fetch info */        
+       serv_puts("INFO");
+       serv_gets(buf);
+       if (buf[0]!='1') return;
+
+       a = 0;
+       while(serv_gets(buf), strcmp(buf,"000")) {
+           switch(a) {
+               case 0:         infobuf->serv_pid = atoi(buf);
+                               break;
+               case 1:         strcpy(infobuf->serv_nodename,buf);
+                               break;
+               case 2:         strcpy(infobuf->serv_humannode,buf);
+                               break;
+               case 3:         strcpy(infobuf->serv_fqdn,buf);
+                               break;
+               case 4:         strcpy(infobuf->serv_software,buf);
+                               break;
+               case 5:         infobuf->serv_rev_level = atoi(buf);
+                               break;
+               case 6:         strcpy(infobuf->serv_bbs_city,buf);
+                               break;
+               case 7:         strcpy(infobuf->serv_sysadm,buf);
+                               break;
+               case 9:         strcpy(infobuf->serv_moreprompt,buf);
+                               break;
+               case 10:        infobuf->serv_ok_floors = atoi(buf);
+                               break;
+               }
+           ++a;
+           }
+
+       }
+
diff --git a/citadel/serverapi.c b/citadel/serverapi.c
deleted file mode 100644 (file)
index 86d4cb9..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-#include <stdlib.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <time.h>
-#include <ctype.h>
-#include <string.h>
-#include <errno.h>
-#include "citadel.h"
-
-struct CtdlServerHandle {
-       char ServerAddress[64];
-       int ServerPort;
-       char ipgmSecret[32];
-       char UserName[32];
-       char Password[32];
-       char InitialRoom[32];
-       int AssocClientSession;
-       };
-
-struct CtdlServerHandle CtdlMyHandle;
-
-
-void logoff(exitcode) {
-       exit(exitcode);
-       }
-
-/*
- * Programs linked against the Citadel server extension library need to
- * be called with the following arguments:
- * 0 - program name (as always)
- * 1 - server address (usually 127.0.0.1)
- * 2 - server port number
- * 3 - internal program secret
- * 4 - user name
- * 5 - user password
- * 6 - initial room
- * 7 - associated client session
- * 
- */
-
-main(argc, argv)
-int argc;
-char *argv[]; {
-       int a;
-       char buf[256];
-
-       /* We're really not interested in stdio */
-       close(0);
-       close(1);
-       close(2);
-
-       /* Bail out if someone tries to run this thing manually */
-       if (argc < 3) exit(1);
-
-       /* Zeroing out the server handle neatly sets the values of
-        * CtdlMyHandle to sane default values
-        */
-       bzero(&CtdlMyHandle, sizeof(struct CtdlServerHandle));
-
-       /* Now parse the command-line arguments fed to us by the server */
-       for (a=0; a<argc; ++a) switch(a) {
-               case 1: strcpy(CtdlMyHandle.ServerAddress, argv[a]);
-                       break;
-               case 2: CtdlMyHandle.ServerPort = atoi(argv[a]);
-                       break;
-               case 3: strcpy(CtdlMyHandle.ipgmSecret, argv[a]);
-                       break;
-               case 4: strcpy(CtdlMyHandle.UserName, argv[a]);
-                       break;
-               case 5: strcpy(CtdlMyHandle.Password, argv[a]);
-                       break;
-               case 6: strcpy(CtdlMyHandle.InitialRoom, argv[a]);
-                       break;
-               case 7: CtdlMyHandle.AssocClientSession = atoi(argv[a]);
-                       break;
-               }
-
-       /* Connect to the server */
-       argc = 3;
-       attach_to_server(argc, argv);
-       serv_gets(buf);
-       if (buf[0] != '2') exit(1);
-
-       /* Set up the server environment to our liking */
-
-       sprintf(buf, "IDEN 0|5|006|CitadelAPI Client");
-       serv_puts(buf);
-       serv_gets(buf);
-
-       if (strlen(CtdlMyHandle.ipgmSecret) > 0) {
-               sprintf(buf, "IPGM %s", CtdlMyHandle.ipgmSecret);
-               serv_puts(buf);
-               serv_gets(buf);
-               }
-
-       if (strlen(CtdlMyHandle.UserName) > 0) {
-               sprintf(buf, "USER %s", CtdlMyHandle.UserName);
-               serv_puts(buf);
-               serv_gets(buf);
-               sprintf(buf, "PASS %s", CtdlMyHandle.Password);
-               serv_puts(buf);
-               serv_gets(buf);
-               }
-
-       sprintf(buf, "GOTO %s", CtdlMyHandle.InitialRoom);
-       serv_puts(buf);
-       serv_gets(buf);
-       if (buf[0] != '2') {
-               serv_puts("GOTO _BASEROOM_");
-               serv_gets(buf);
-               }
-
-       /* Now do the loop. */
-       sleep(10);
-
-       /* Clean up gracefully and exit. */
-       serv_puts("QUIT");
-       exit(0);
-       }
index ed6cd41e28d272bb496cefaad4187273079a6dfc..3666a3026d4d59c7c8c8f63b612641a62871c5cf 100644 (file)
@@ -20,6 +20,37 @@ straightforward.
 
 
 
+ WRITING A SERVER-SIDE APPLICATION
+ ---------------------------------
+  
+ Server-side applications written to the CitadelAPI may be written in any
+language, as long as the "citadelapi" library is linked in.  Unlike normal
+user-mode programs, you must not declare a main() function, and there will be
+no stdin/stdout/stderr available.  Instead, you must declare this:
+ void CtdlMain() {
+                 /* your program's main loop goes here */
+                 }
+ When a server-side application is started, the main() loop inside the
+CitadelAPI is invoked.  This will perform all of the necessary initialization
+(attach to the server, authenticate, identify itself, etc.) and then call
+your program's CtdlMain() loop.
+   
+ By the time CtdlMain() receives control, there will be two public symbols
+you can access, which point to data structures containing various information
+that may be useful to the program:
+ extern struct CtdlServerHandle CtdlAppHandle;
+ extern struct CtdlServInfo CtdlAppServInfo;
+
+ CtdlAppHandle contains various information about how and why this program is
+connected to the server.  CtdlAppSrvInfo contains various static information
+about the server the program is connected to.  Both CtdlServerHandle and
+CtdlServInfo are defined in the standard header files.
  ERROR HANDLING
  --------------