home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume21 / coda / part02.Z / part02 / server.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-04-08  |  9.4 KB  |  499 lines

  1. /*
  2. **  Copyright 1989 BBN Systems and Technologies Corporation.
  3. **  All Rights Reserved.
  4. **  This is free software, and may be distributed under the terms of the
  5. **  GNU Public License; see the file COPYING for more details.
  6. **
  7. **  Main driver for CODA server.
  8. */
  9. #define MAINLINE
  10. #include "server.h"
  11. #include <pwd.h>
  12. #include <signal.h>
  13. #include <setjmp.h>
  14. #include <sys/stat.h>
  15. #include <sys/socket.h>
  16. #include <netinet/in.h>
  17. #include <netdb.h>
  18. #ifdef    RCSID
  19. static char RCS[] =
  20.     "$Header: server.c,v 2.0 90/03/23 14:41:55 rsalz Exp $";
  21. #endif    /* RCSID */
  22.  
  23.  
  24. /*
  25. **  A command has a text name, an internal value, and a help message.
  26. */
  27. typedef struct _TABLE {
  28.     char    *Name;
  29.     COMMAND    Value;
  30.     char    *Help;
  31. } TABLE;
  32.  
  33. char        *LogFile = LOGFILE;    /* Name of the log file        */
  34. char        UnknownHost[] = GUESTHOST; /* For unknown hosts        */
  35.  
  36. STATIC jmp_buf    Context;        /* When the bell rings        */
  37. STATIC BOOL    LoggedIn;        /* Did we get a USER command?    */
  38. STATIC BOOL    Testing;        /* Are we in test mode?        */
  39.  
  40. STATIC TABLE    Commands[] = {        /* List of commands        */
  41.     {    "GOTO",        CMDgoto,    "Change to specified directory" },
  42.     {    "EXIT",        CMDquit,    "Shut down server" },
  43.     {    "HELP",        CMDhelp,    "Print this status report" },
  44.     {    "HOST",        CMDhost,    "Specify name of destination host" },
  45.     {    "MESG",        CMDmesg,    "Send message to log file" },
  46.     {    "LIST",        CMDlist,    "List status of files [in block]" },
  47.     {    "QUIT",        CMDquit,    "Shut down server" },
  48.     {    "READ",        CMDread,    "Read control file, name optional" },
  49.     {    "ROOT",        CMDroot,    "Set root for relative pathnames" },
  50.     {    "SEND",        CMDsend,    "Start file-sending protocol" },
  51.     {    "USER",        CMDuser,    "Log in a specified user" },
  52.     {    "",        CMD_time,    NULL },
  53.     { NULL }
  54. };
  55.  
  56.  
  57.  
  58. /*
  59. **  Return a perror-style string.
  60. */
  61. char *
  62. strerror(e)
  63.     int        e;
  64. {
  65.     extern int    sys_nerr;
  66.     extern char    *sys_errlist[];
  67.     char    buff[20];
  68.  
  69.     if (e < 0 || e > sys_nerr) {
  70.     (void)sprintf(buff, "Error code %d\n", e);
  71.     return buff;
  72.     }
  73.     return sys_errlist[e];
  74. }
  75.  
  76.  
  77.  
  78. /*
  79. **  Send a message saying a command was successful.
  80. */
  81. void
  82. Ack(p)
  83.     char    *p;
  84. {
  85.     (void)printf("ACK-%s\r\n", p ? p : "Done");
  86.     (void)fflush(stdout);
  87. }
  88.  
  89.  
  90. /*
  91. **  Send a message saying that a command failed.
  92. */
  93. void
  94. Nack(p)
  95.     char    *p;
  96. {
  97.     (void)printf("NAK-%s\r\n", p ? p : strerror(errno));
  98.     (void)fflush(stdout);
  99. }
  100.  
  101.  
  102. /*
  103. **  Send an information message.  The client shoujld pass these on to
  104. **  the user.
  105. */
  106. void
  107. Message(p)
  108.     char    *p;
  109. {
  110.     (void)printf("INF %s\r\n", p ? p : "Never mind");
  111. }
  112.  
  113.  
  114. /*
  115. **  Send a data message.  This is for the client program.
  116. */
  117. void
  118. Data(p)
  119.     char    *p;
  120. {
  121.     if (p)
  122.     (void)printf("DAT %s\r\n", p);
  123. }
  124.  
  125.  
  126.  
  127. /*
  128. **  When that bell rings, get out of here!
  129. */
  130. STATIC CATCHER
  131. AlarmCatch()
  132. {
  133.     longjmp(Context, 1);
  134.     /* NOTREACHED */
  135. }
  136.  
  137.  
  138. /*
  139. **  Read a line from the client.  Quit if it times out.  Otherwise
  140. **  parse the first word as a command, stuff the rest of the line as
  141. **  a possible argument to the command, and return the command's value.
  142. */
  143. STATIC COMMAND
  144. ReadLine(arg, size)
  145.     char        *arg;
  146.     int            size;
  147. {
  148.     register char    *p;
  149.     register char    *q;
  150.     register TABLE    *T;
  151.     char        buff[SIZE];
  152.  
  153.     if (setjmp(Context) == 1)
  154.     return CMD_time;
  155.  
  156.     for ( ; ; ) {
  157.     /* Timed-out read. */
  158.     (void)alarm(TIMEOUT);
  159.     p = fgets(buff, sizeof buff, stdin);
  160.     (void)alarm(0);
  161.     if (p == NULL)
  162.         return CMDquit;
  163.  
  164.     /* Kill the terminator. */
  165.     if ((p = strchr(buff, '\r')) || (p = strchr(buff, '\n')))
  166.         *p = '\0';
  167.  
  168.     /* Skip whitespace, ignore totally blank lines. */
  169.     for (p = buff; *p && WHITE(*p); p++)
  170.         ;
  171.     if (*p == '\0')
  172.         continue;
  173.  
  174.     /* Find first word. */
  175.     for (q = p; *q && !WHITE(*q); q++)
  176.         ;
  177.  
  178.     /* Snip off first word, copy rest of line to argument. */
  179.     if (*q) {
  180.         for (*q = '\0'; *++q && WHITE(*q); )
  181.         ;
  182.         (void)strncpy(arg, q, size);
  183.         arg[size - 1] = '\0';
  184.     }
  185.     else
  186.         arg[0] = '\0';
  187.     Uppercase(p);
  188.  
  189.     /* Find first word in the command table. */
  190.     for (T = Commands; T->Name; T++)
  191.         if (EQ(T->Name, p))
  192.         return T->Value;
  193.  
  194.     Nack("Unknown command");
  195.     }
  196. }
  197.  
  198.  
  199.  
  200.  
  201. /*
  202. **  Get the name of the host where the client it.
  203. */
  204. STATIC void
  205. GetClientHostname()
  206. {
  207.     struct hostent    *hp;
  208.     struct sockaddr_in    venial;
  209.     char        buff[SIZE];
  210.     int            size;
  211.  
  212.     if (isatty(0))
  213.     /* Obviously a local call... */
  214.     TheHost = gethostname(buff, sizeof buff) < 0 ? NULL : buff;
  215.     else {
  216.     size = sizeof venial;
  217.     if (getpeername(0, (struct sockaddr *)&venial, &size) < 0)
  218.         TheHost = NULL;
  219.     else {
  220.         hp = gethostbyaddr((char *)&venial.sin_addr,
  221.                 sizeof venial.sin_addr, AF_INET);
  222.         TheHost = hp ? hp->h_name : NULL;
  223.     }
  224.     }
  225.  
  226.     TheHost = TheHost ? COPY(TheHost) : COPY(UnknownHost);
  227.     Uppercase(TheHost);
  228. }
  229.  
  230.  
  231. /*
  232. **  Read a CODA control file to see if its valid.
  233. */
  234. STATIC void
  235. CheckCodafile(p)
  236.     char    *p;
  237. {
  238.     ResetStorage();
  239.     if (!yyopen(TRUE, p)) {
  240.     Nack((char *)NULL);
  241.     exit(1);
  242.     }
  243.     if (yyparse() != BADPARSE && !HaveErrors)
  244.     exit(0);
  245.     Nack("Syntax errors found");
  246.     exit(1);
  247. }
  248.  
  249.  
  250. /*
  251. **  Verify a user's name and password.
  252. */
  253. STATIC void
  254. Login(p)
  255.     char        *p;
  256. {
  257.     static char        BAD[] = "Bad username or password";
  258.     struct passwd    *pwd;
  259.     char        *pass;
  260.     char        buff[SIZE];
  261.  
  262.     /* Split at the space into a name:password pair. */
  263.     if ((pass = strchr(p, ' ')) == NULL) {
  264.     Nack(BAD);
  265.     return;
  266.     }
  267.     *pass++ = '\0';
  268.  
  269.     /* Valid name with the right password? */
  270.     if ((pwd = getpwnam(p)) == NULL) {
  271.     Nack(BAD);
  272.     return;
  273.     }
  274.     if (!EQ(pwd->pw_passwd, crypt(pass, pwd->pw_passwd))) {
  275.     Nack(BAD);
  276.     return;
  277.     }
  278.  
  279.     /* Change identity. */
  280.     if (initgroups(p, pwd->pw_gid) < 0 || setuid(pwd->pw_uid) < 0) {
  281.     Nack((char *)NULL);
  282.     return;
  283.     }
  284.  
  285.     /* Done; log and acknowledge it. */
  286.     LoggedIn = TRUE;
  287.     (void)sprintf(buff, "Logged in %s", p);
  288.     LogText(buff);
  289.     Ack(buff);
  290. }
  291.  
  292.  
  293. /*
  294. **  Print a usage message and exit.
  295. */
  296. STATIC void
  297. Usage()
  298. {
  299.     (void)fprintf(stderr, "Usage: codaserver %s\n",
  300.         "[-c] [-lLogfile] [-rCodafile] [-t]");
  301.     exit(1);
  302. }
  303.  
  304.  
  305. /* ARGSUSED1 */
  306. main(ac, av)
  307.     int            ac;
  308.     char        *av[];
  309. {
  310.     static char        LOGIN[] = "Login first";
  311.     register BOOL    DidRead;
  312.     register char    *p;
  313.     register COMMAND    C;
  314.     register int    i;
  315.     register TABLE    *T;
  316.     char        arg[SIZE];
  317.  
  318.     /* Parse JCL. */
  319.     while ((i = getopt(ac, av, "cl:r:t")) != EOF)
  320.     switch (i) {
  321.     default:
  322.         Usage();
  323.         /* NOTREACHED */
  324.     case 'c':
  325.         Copyright();
  326.         exit(0);
  327.         /* NOTREACHED */
  328.     case 'l':
  329.         LogFile = optarg;
  330.         break;
  331.     case 'r':
  332.         CheckCodafile(optarg);
  333.         /* NOTREACHED */
  334.     case 't':
  335.         /* Play it safe... */
  336.         if (isatty(0)) {
  337.         Testing = TRUE;
  338.         LoggedIn = TRUE;
  339.         (void)printf("Testing...\n");
  340.         }
  341.         break;
  342.     }
  343.  
  344.     /* Check for other arguments. */
  345.     ac -= optind;
  346.     av += optind;
  347.     if (ac)
  348.     Usage();
  349.  
  350.     if (!Testing) {
  351.     /* Set up IO descriptors, be robust in the face of varying inetd's. */
  352.     (void)close(1);
  353.     (void)close(2);
  354.     (void)dup(0);
  355.     (void)dup(0);
  356.     }
  357.  
  358.     /* Do various initializations. */
  359.     GetClientHostname();
  360.     (void)signal(SIGALRM, AlarmCatch);
  361.     ResetStorage();
  362.     LogOpen();
  363.  
  364.     /* Tell client we're here. */
  365.     (void)sprintf(arg, "Hello %s; how are you today?", TheHost);
  366.     Ack(arg);
  367.  
  368.     /* Read and dispatch loop. */
  369.     DidRead = FALSE;
  370.     while ((C = ReadLine(arg, sizeof arg)) != CMDquit)
  371.     switch (C) {
  372.     default:
  373.         Nack("Unimplemented command");
  374.         break;
  375.  
  376.     case CMDgoto:
  377.         if (!LoggedIn)
  378.         Nack(LOGIN);
  379.         else if (arg[0] == '\0')
  380.         Nack("Directory missing");
  381.         else if (chdir(arg) < 0)
  382.         Nack((char *)NULL);
  383.         else
  384.         Ack((char *)NULL);
  385.         break;
  386.  
  387.     case CMDhelp:
  388.         for (T = Commands; T->Name; T++)
  389.         if (T->Help) {
  390.             (void)sprintf(arg, "%s\t%s", T->Name, T->Help);
  391.             Message(arg);
  392.         }
  393.         Ack((char *)NULL);
  394.         break;
  395.  
  396.     case CMDhost:
  397.         if (!Testing)
  398.         Nack("I already know who you are");
  399.         else {
  400.         if (TheHost) {
  401.             free(TheHost);
  402.             TheHost = NULL;
  403.         }
  404.         Uppercase(arg);
  405.         if (HostWasDeclared(arg)) {
  406.             TheHost = COPY(arg);
  407.             Ack((char *)NULL);
  408.         }
  409.         else
  410.             Nack("Host undefined");
  411.         }
  412.         break;
  413.  
  414.     case CMDlist:
  415.         if (!LoggedIn)
  416.         Nack(LOGIN);
  417.         else if (TheHost == NULL)
  418.         Nack("No host specified");
  419.         else if (!DidRead)
  420.         Nack("Use READ first");
  421.         else
  422.         /* ListFiles() does its own Ack or Nack. */
  423.         ListFiles(arg);
  424.         break;
  425.  
  426.     case CMDmesg:
  427.         LogText(arg);
  428.         Ack((char *)NULL);
  429.         break;
  430.  
  431.     case CMDread:
  432.         if (!LoggedIn)
  433.         Nack(LOGIN);
  434.         else {
  435.         Rooted = FALSE;
  436.         AllowBinaries = TRUE;
  437.         ResetStorage();
  438.         DidRead = FALSE;
  439.         p = arg[0] ? arg : CONTROLFILE;
  440.         if (!yyopen(FALSE, p))
  441.             Nack((char *)NULL);
  442.         else {
  443.             if (yyparse() == BADPARSE || HaveErrors)
  444.             Nack("Can't parse file");
  445.             else {
  446.             LogReadfile(p);
  447.             if (!HostWasDeclared(TheHost)) {
  448.                 (void)sprintf(arg, "Host \"%s\" not in the file.",
  449.                     TheHost);
  450.                 Message(arg);
  451.             }
  452.             Ack((char *)NULL);
  453.             DidRead = TRUE;
  454.             }
  455.             yyclose();
  456.         }
  457.         }
  458.         break;
  459.  
  460.     case CMDroot:
  461.         if (Rooted)
  462.         Nack("Permission denied");
  463.         else {
  464.         if (TheRoot)
  465.             free(TheRoot);
  466.         TheRoot = arg[0] ? COPY(arg) : NULL;
  467.         Ack((char *)NULL);
  468.         }
  469.         break;
  470.  
  471.     case CMDsend:
  472.         if (!LoggedIn)
  473.         Nack(LOGIN);
  474.         else if (!DidRead)
  475.         Nack("Use READ first");
  476.         else if (arg[0])
  477.         /* SendFile() does its own Ack or Nack. */
  478.         SendFile(arg);
  479.         else
  480.         Nack("Filename missing");
  481.         break;
  482.  
  483.     case CMDuser:
  484.         /* Login() does its own Ack or Nack. */
  485.         Login(arg);
  486.         break;
  487.  
  488.     case CMD_time:
  489.         LogClose("timeout");
  490.         exit(0);
  491.         /* NOTREACHED */
  492.  
  493.     }
  494.  
  495.     LogClose("quit");
  496.     exit(0);
  497.     /* NOTREACHED */
  498. }
  499.