Example: Server program

/**********************************************************************/
/*                                                                    */
/* Name:           server.c                                           */
/*                                                                    */
/* Description:    This program is a server for starting interactive  */
/*                 qsh sessions on remote clients.  The program       */
/*                 listens for connections from clients.  When a      */
/*                 connection is accepted, it reads the user name     */
/*                 and password of the client.  It then swaps to the  */
/*                 the specified user profile and spawns a new        */
/*                 process running the qsh shell interpreter that     */
/*                 handles the connection.                            */
/*                                                                    */
/* Parameters:  1. Port number to listen for connections on.          */
/*                                                                    */
/* Notes:       1. The user name and password are sent as plain text  */
/*                 from the client.                                   */
/*              2. The user profile running this program must have    */
/*                 authority to the QSYGETPH, QSYRLSPH, and           */
/*                 QWTSETP APIs.                                      */
/*              3. You will need to change the value of the NLSPATH   */
/*                 environment variable if your system is using a     */
/*                 different language than 2924.                      */
/*                                                                    */
/**********************************************************************/

/**********************************************************************/
/* Includes                                                           */
/**********************************************************************/

#include <stdio.h>           /* fopen(), vfprintf() */
#include <sys/socket.h>      /* socket(), bind(), and so on. */
#include <netinet/in.h>      /* sockaddr_in, INADDR_ANY, and so on */
#include <arpa/inet.h>       /* inet_ntoa() */
#include <spawn.h>           /* spawn() */
#include <unistd.h>          /* close(), read(), and so on */
#include <stdlib.h>          /* exit()*/
#include <stdarg.h>          /* va_start(), va_end() */
#include <qp0z1170.h>        /* Qp0zInitEnv() */
#include <qsygetph.h>        /* QSYGETPH() */
#include <qwtsetp.h>         /* QWTSETP() */
#include <qsyrlsph.h>        /* QSYRLSPH() */
#include <qusec.h>           /* Qus_EC_t */
#include <pwd.h>             /* getpwnam() */
#include <ctype.h>           /* toupper() */
#include <time.h>            /* ctime(), time() */
#include <except.h>          /* Exception and cancel handling */
#include <errno.h>           /* errno and constants */

/**********************************************************************/
/* Constants                                                          */
/**********************************************************************/

#define DEFAULT_BUF 4096
#define DEFAULT_PORT 6042
#define NULL_PH "\0\0\0\0\0\0\0\0\0\0\0\0"
#define PH_SIZE 12
#define NAME_SIZE 11
#undef PATH_MAX
#define PATH_MAX 4096

/**********************************************************************/
/* Global Variables                                                   */
/**********************************************************************/

/* For logging errors */
FILE *log_fp;
char log_file[] = "/tmp/qsh_server.log";
char log_buffer[DEFAULT_BUF];

/**********************************************************************/
/* Function Prototypes                                                */
/**********************************************************************/

int strtoupper(char *);
int GetString(int, char *, size_t);
void LogError(char *, ...);
void SendError(int, char *, ...);
void CleanupHandler(_CNL_Hndlr_Parms_T *);

int main(int argc, char *argv[])
{
  int sfd;                    /* Server's listening socket */
  int cfd;                    /* Socket connected to client */
  int on=1;                   /* Flag for setsockopt() */
  struct sockaddr_in my_addr; /* Address server binds to */
  struct sockaddr_in client_addr; /* Addrress of connected client */
  int client_addr_len;        /* Length of client's socket address */
  unsigned short port;        /* Server's TCP port */
  char server_ph[PH_SIZE+1] = NULL_PH; /* Server's profile handle */
  char client_ph[PH_SIZE+1] = NULL_PH; /* Client's profile handle */
  char profile[NAME_SIZE];    /* User profile read from client */
  char password[NAME_SIZE];   /* Password read from client */
  char sy_profile[NAME_SIZE]; /* User profile for i5/OS(TM) APIs */
  char sy_password[NAME_SIZE]; /* Password for i5/OS(TM) APIs */
  char server_profile[NAME_SIZE] = "*CURRENT  ";
  char no_pwd[NAME_SIZE]         = "*NOPWD    ";
  struct passwd *cpw;         /* User information for client */
  Qus_EC_t error = { sizeof(Qus_EC_t), 0 }; /* Error code for SPIs */

  /* Parameters for spawn() to shell process */
  char qsh_pgm[] = "/QSYS.LIB/QSHELL.LIB/QZSHSH.PGM";
  char *args[5];              /* Argument array */
  char *envs[10];             /* Environment variable array */
  int fd_count;               /* Number of descriptors */
  int fd_map[3];              /* Map of descriptors */
  struct inheritance inherit; /* Inheritance options */
  char server_dir[] = "/";    /* Default current working directory */
  
  /* Environment variables */
  char home_var[PATH_MAX+10];
  char logname_var[NAME_SIZE+10];
  char path_var[] = "PATH=/usr/bin:";
  char stdio_var[] = "QIBM_USE_DESCRIPTOR_STDIO=I";
  char terminal_type_var[] = "TERMINAL_TYPE=REMOTE";
  char nlspath_var[] = "NLSPATH=/QIBM/ProdData/OS400/Shell/MRI2924/%N";

  volatile _INTRPT_Hndlr_Parms_T ca; /* For exception handler */
  
  /********************************************************************/
  /* Process the input parameters. */
  /********************************************************************/

  /* Use the default port if one is not specified. */
  if (argc < 2) {
    port = DEFAULT_PORT;
  }

  else {
    port = atoi(argv[1]);
  }
  
  /********************************************************************/
  /* Initialize the server environment. */
  /********************************************************************/

  /* Initialize for environment variables. */
  Qp0zInitEnv();

  /* Change to default directory. */
  chdir(server_dir);
  
  /* Initialize the server's profile handle. */
  QSYGETPH(server_profile, no_pwd, server_ph, &error);
  if (error.Bytes_Available != 0) {
    LogError("Could not get profile handle for server, "
             "QSYGETPH() failed with exception %7.7s\n",
             error.Exception_Id);
    exit(1);
  }

  /********************************************************************/
  /* Set up the listening socket. */
  /********************************************************************/

  /* Create a socket. */
  if ((sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0) {
    LogError("socket() failed, errno=%d\n", errno);
    exit(1);
  }

  #pragma cancel_handler(CleanupHandler, sfd)
  #pragma exception_handler(Cleanup, ca, _C1_ALL, _C2_ALL)
  
  /* Allow re-use of this socket address. */
  if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on,
                 sizeof(int)) != 0) {
    LogError("setsockopt() failed, errno=%d\n", errno);
    exit(1);
  }
  
  /* Bind to a port. */
  memset(&my_addr, '\0', sizeof(my_addr));
  my_addr.sin_family = AF_INET;
  my_addr.sin_port = port;
  my_addr.sin_addr.s_addr = INADDR_ANY;
  if (bind(sfd, (struct sockaddr *)&my_addr, sizeof(my_addr)) != 0) {
    LogError("bind() failed for port %d, errno=%d\n", port, errno);
    close(sfd);
    exit(1);
  }

  /* Make this a listening socket. */
  if (listen(sfd, 10) != 0) {
    LogError("listen() failed, errno=%d\n", errno);
    close(sfd);
    exit(1);
  }

  /********************************************************************/
  /* Accept connections from clients. */
  /********************************************************************/

  while (1) {
    if ((cfd = accept(sfd, NULL, 0)) < 0) {
      LogError("accept() failed, errno=%d\n", errno);
      close(sfd);
      exit(1);
    }

    /* Read the user profile and password from the client.  The client
       sends two null-terminated strings - the first one is the user
       profile and the second one is the password. */
    if (GetString(cfd, profile, 11) != 0) {
      getpeername(cfd, (struct sockaddr *)&client_addr, &client_addr_len);
      LogError("Could not read profile from client at %s, port %hu\n",
               inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
      close(cfd);
      continue;
    }
    
    if (GetString(cfd, password, 11) != 0) {
      getpeername(cfd, (struct sockaddr *)&client_addr, &client_addr_len);
      LogError("Could not read password from client  at %s, port %hu\n",
               inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
      close(cfd);
      continue;
    }
 
    /* Check for the special values that turn off password checking in QSYGETPH(). */
    if ((profile[0] == '*') || (password[0] == '*')) {
      getpeername(cfd, (struct sockaddr *)&client_addr, &client_addr_len);
      LogError("Invalid password sent from client at %s, port %hu\n",
               inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
      close(cfd);
      continue;
    }
      
    /* QSYGETPH() requires that the profile be exactly ten characters, 
       left-aligned in the field, and padded with blanks. */
    strtoupper(profile);
    sprintf(sy_profile, "%-10.10s", profile);
    
    /* Get the profile handle for the client's user profile. */
    QSYGETPH(sy_profile, password, client_ph, &error, strlen(password), 0);
    if (error.Bytes_Available != 0) {
      LogError("Could not get profile handle for profile %s, "
               "QSYGETPH() failed with exception %7.7s\n",
               sy_profile, error.Exception_Id);
      SendError(cfd, "Could not get profile handle for profile %s\n",
                sy_profile);
      close(cfd);
      continue;
    }

    /* Switch to client's user profile. */
    QWTSETP(client_ph, &error);
    if (error.Bytes_Available != 0) {
      LogError("Could not switch to profile %s, "
               "QWTSETP() failed with exception %7.7s\n",
               sy_profile, error.Exception_Id);
      SendError(cfd, "Could not switch to profile %s\n", sy_profile);
      QSYRLSPH(client_ph, NULL);
      close(cfd);
      continue;
    }
    
    /* Get the info for this user profile. */
    if ((cpw = getpwnam(profile)) == NULL) {
      /* Log error. */
      LogError("Could not retrieve information for profile %s, "
               "getpwnam() failed with errno=%d\n",
               profile, errno);
      SendError(cfd, "Could not retrieve information for profile %s\n",
                profile);

      /* Switch back to the server's user profile. */
      QWTSETP(server_ph, &error);
      if (error.Bytes_Available != 0) {
        LogError("Could not switch back to server's profile, "
                 "QWTSETP() failed with exception %7.7s\n",
                 error.Exception_Id);
        break;
      }

      /* Release the client's profile handle. */
      QSYRLSPH(client_ph, NULL);
      if (error.Bytes_Available != 0) {
        LogError("Could not release client's profile handle, "
                 "QSYRLSPH() failed with exception %7.7s\n",
                 error.Exception_Id);
        break;
      }
      close(cfd);
      continue;
    }

    /* Build the file descriptor map for the child. */
    fd_count = 3;
    fd_map[0] = cfd;
    fd_map[1] = cfd;
    fd_map[2] = cfd;
    
    /* Build the argv array for the child. */
    args[0] = qsh_pgm;
    args[1] = "-login";      /* Do login processing */
    args[2] = "-s";          /* Take input from stdin */
    args[3] = "-i";          /* Run as an interactive shell */
    args[4] = NULL;

    /* Build the environ array for the child. */
    sprintf(home_var, "HOME=%s", cpw->pw_dir);
    sprintf(logname_var, "LOGNAME=%s", cpw->pw_name);
    envs[0] = home_var;
    envs[1] = logname_var;
    envs[2] = path_var;
    envs[3] = stdio_var;
    envs[4] = terminal_type_var;
    envs[5] = nlspath_var;
    envs[6] = NULL;
    
    /* Set up the inheritance structure. */
    memset(&inherit, '\0', sizeof(struct inheritance));
    inherit.flags = SPAWN_SETTHREAD_NP;
    inherit.pgroup = SPAWN_NEWPGROUP;

    /* Change to the home directory for the client.  The child process
       inherits this as its current working directory. */
    chdir(cpw->pw_dir);
    
    /* Start a child process running the shell interpreter. */
    if (spawn(args[0], fd_count, fd_map, &inherit, args, envs) < 0) {
      LogError("Could not start qsh process, spawn() failed with "
               "errno=%d\n", errno);
      SendError(cfd, "Could not start qsh process\n");
    }

    /* Clean up for the next connection. */
    chdir(server_dir);
    close(cfd);
    
    /* Switch back to server's user profile. */
    QWTSETP(server_ph, &error);
    if (error.Bytes_Available != 0) {
      LogError("Could not switch back to server's profile, "
               "QWTSETP() failed with exception %7.7s\n",
               error.Exception_Id);
      break;
    }

    /* Release the client's profile handle. */
    QSYRLSPH(client_ph, &error);
    if (error.Bytes_Available != 0) {
      LogError("Could not release client's profile handle, "
               "QSYRLSPH() failed with exception %7.7s\n",
               error.Exception_Id);
      break;
    }
  } /* End of while */

  /* Clean up. */
  close(sfd);
  
  #pragma disable_handler /* Exception handler */
  #pragma disable_handler /* Cancel handler */

  exit(0);
  return 0;

  /* Exception handler */
  Cleanup:

  LogError("Unexpected exception %7.7s\n", ca.Msg_Id);
  close(sfd);
  exit(1);
} /* End of main() */


/*
 * Convert a string to uppercase.
 */

int
strtoupper(char *string)
{ 
  for ( ; *string != '\0'; ++string) 
    *string = toupper(*string);

  return 0;
} /* End of strtoupper() */


/*
 * Read a string from a socket.
 */

int
GetString(int fd, char *buffer, size_t nbytes)
{
  char c;
  do {
    if (read(fd, &c, 1) != 1) {
      return -1;
    }
    *buffer++ = c;
    if (--nbytes == 0) {
      return 0;
    }
  } while (c != '\0');

  return 0;
} /* End of GetString() */


/*
 * Write an error message to the log file.
 */

void LogError(char *format, ...)
{
  va_list ap;
  time_t now;                /* Time stamp */

  /* If needed, open the log file. */
  if (log_fp == NULL) {
    log_fp = fopen(log_file, "w");
    if (log_fp == NULL) {
      return;
    }
  }

  /* Write timestamp to the log file. */
  now=time(NULL);
  fprintf(log_fp, "\n%s", ctime(&now));
  
  /* Write the formatted string to the log file. */
  va_start(ap, format);
  vfprintf(log_fp, format, ap);
  va_end(ap);

  /* Flush output to log file. */
  fflush(log_fp);
  
  return;
} /* End of LogError() */


/*
 * Send an error message to the client.
 */

void SendError(int fd, char *format, ...)
{
  va_list ap;

  /* Build the formatted string. */
  va_start(ap, format);
  vsprintf(log_buffer, format, ap);
  va_end(ap);
  
  /* Write the formatted string. */
  write(fd, log_buffer, strlen(log_buffer));
  
  return;
} /* End of SendError() */


/*
 * Handler to clean up when the program is canceled.
 */

void CleanupHandler(_CNL_Hndlr_Parms_T *cancel_info)
{
  int sfd;
  sfd = *((int *)cancel_info->Com_Area);
  close(sfd);
} /* End of CleanupHandler() */

Note: By using the code examples, you agree to the terms of the Code license and disclaimer information.