492 lines
16 KiB
HTML
492 lines
16 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
|
|
<html>
|
|
<head>
|
|
<meta name="generator" content="HTML Tidy, see www.w3.org">
|
|
<meta http-equiv="Content-Type" content=
|
|
"text/html; charset=utf-8">
|
|
<title>Example: Server program</title>
|
|
<LINK rel="stylesheet" type="text/css" href="../rzahg/ic.css">
|
|
</HEAD>
|
|
<body bgcolor="#FFFFFF">
|
|
<!-- Java sync-link -->
|
|
<SCRIPT LANGUAGE="Javascript" SRC="../rzahg/synch.js" TYPE="text/javascript"></SCRIPT>
|
|
|
|
|
|
|
|
<h2>Example: Server program</h2>
|
|
|
|
|
|
<pre>
|
|
/**********************************************************************/
|
|
/* */
|
|
/* 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<SUP>(TM)</SUP> APIs */
|
|
char sy_password[NAME_SIZE]; /* Password for i5/OS<SUP>(TM)</SUP> 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() */
|
|
</pre>
|
|
<p><strong>Note:</strong> By using the code examples, you agree to the terms of the <a href="codedisclaimer.htm">Code license and disclaimer information</a>.</p>
|
|
</body>
|
|
</html>
|
|
|