QSHELL とは UNIX 環境で使用されるコマンド・スクリプトの処理環境である。
System i で言えば、まさに「コマンド入力」画面そのものと同じである。
System i では UNIX の SHELL コマンドを実行するための SHELL 環境が提供されている。
Java の開発者であれば良くご存知であろう。
さてIBM インフォメーション・センターに「qshセッションに接続するリモート・クライアントの使用」と題する
サンプル・ソースが公開されている。
これは QSHELL がどのような処理構造で動作しているのかをユーザーに学習させるためのサンプルである
と思われる。ある目的があってサンプル・ソースを試してみた。
ところが残念なことにこれらのサンプルには誤りがあり、そのままでは動作しない。コンパイルも成功しない。
しかしある個人サイトには「コンパイルして実行した」と紹介されていた。
しかも PC でコンパイルして, … と書かれていたがコンパイルの詳細もプラットフォーム(Windows/Linux, …)
も書かれていない。本当にコンパイルができたのだろうか ?
実はIBM のミスで変数 2 つが定義されていないのでコンパイルが通るわけはないのだが
「コンパイルして実行した」とその個人サイトでは紹介されていた。
さらにソースを良く見ると AIX と Linux しか対応していない記述であり、Windows で
コンパイルしても全くエラーとなってコンバイルは通らない。
ところがその個人サイトでのハード・コピー画面は DOSコマンド・プロンプトであり
Wondows であるようであった。一体、彼女はどのようにしてコンパイルしたのであろうか ?
実行結果の画面も紹介されていた。どのようにしたのかは不思議である。
そこでここでは
- コンパイル・エラーを無くすための変数の定義の追加
- System i 上だけで実行できる Cソースの紹介
- サンプル・プログラムの実行方法
- QSHELLサンプル・ソースが示している処理構造
を紹介する。
■ コンパイル・エラーを無くすための変数の定義の追加
最初にコンパイル・エラーとなるのはサンプル・クライアント・ソースに
次の2つの変数の定義が漏れているからである。
- extern int optind;
- extern char* optarg;
■ System i 上だけで実行できる Cソースの紹介
実際のサンプル・クライアント・ソースは System i ではない別のASCIIプラット・フォームでの
実行を前提として書かれているため EBCDIC/ASCII 変換を使用しているが
System i 上での実行用に書き換えたため、 不要な変換処理は除去してある。
また元のサンプル・クライアント・ソースは終わり方を示していないため QUIT という文字列を
入力するとプログラムが終了するような処理を追加している。
さらにいくつかのコメントも日本語としてわかりやすくなっている。
( 先の個人サイトでは本当に実行できたのか疑念が残る)
作成したサンプル・ソース
【QSH_SERVER : サンプル・サーバー・プログラム】
/********************************************************************/
/* */
/* QSH_SERVER : QSHELL セッション・サーバー */
/* */
/* [ 原典 ] QZSHSH IN IBM INFOMATION CENTER */
/* */
/* [ 記述 ] This program is a server for starting interactive */
/* qsh sessions on remote clients. */
/* This 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 specified user profile and spawn */
/* a new process running the qsh shell interpreter that */
/* handle the connection. */
/* */
/* [ パラメータ ] 1. Port number to listen for connection on. */
/* */
/* [ 注記 ] 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 */
/* enviroment valuable if your system is using a */
/* diffrent language than 2924. */
/* */
/********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <arpa/inet.h>
#include <spawn.h>
#include <unistd.h>
#include <stdarg.h>
#include <qp0z1170.h> /* QpOzInitEnv(), putenv */
#include <qsygetph.h> /* QSYGETPH() */
#include <qsyrlsph.h> /* QSYRLSPH() */
#include <qusec.h> /* Qus_EC_t */
#include <pwd.h> /* getpwnam() */
#include <ctype.h> /* toupper */
#include <time.h>
#include <except.h> /* Exception and cancel handling */
#include <errno.h>
#include <QSYGETPH.h>
#include <QWTSETP.h>
#include <QSYRLSPH.h>
/*************************************************************/
/* 固 定 定 数 */
/*************************************************************/
#define TRUE 0
#define FALSE -1
#define DEFAULT_BUF 4096
#define DEFAULT_PORT 6042
#define NULL_PH "・・・・・・・・・・・・"
#define PH_SIZE 12
#define NAME_SIZE 11
#undef PATH_MAX
#define PATH_MAX 4096
/*************************************************************/
/* グ ロ ー バ ル 変 数 */
/*************************************************************/
/* For logging errors */
FILE *log_fp;
char log_file[] = "/tmp/qsh_server.log";
char log_buffer[DEFAULT_BUF];
/*************************************************************/
/* 内 部 使 用 関 数 */
/*************************************************************/
int strtoupper(char*);
int GetString(int, char*, size_t);
void LogError(char*, ...);
void SendError(int, char*, ...);
void ClearHandler(_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; /* Address 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 APIs */
char sy_password[NAME_SIZE]; /* Password for i/OS 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 APIs */
/* spawn 関数のためのパラメータ */
char qsh_pgm[] = "/QSYS.LIB/QSHELL.LIB/QZSHSH.PGM";
char *args[5]; /* Argment array */
char *envs[10]; /* Enviroment 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 */
/*( 環境変数 )*/
char home_var[PATH_MAX+10];
char logname_var[NAME_SIZE+10];
char path_var[] = "PATH=/usr/bin;,:/QOpenSys/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/MRI2962/%N";
volatile _INTRPT_Hndlr_Parms_T ca;
/*************************************************************/
/* 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 enviroment. */
/*************************************************************/
/* Initialize for enviroment 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瀟",
error.Exception_Id);
exit(1);
}
/*************************************************************/
/* Set up listening socket. */
/*************************************************************/
/* Create socket. */
if((sfd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0){
LogError("socket() failed, error = %d瀟", 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, error = %d瀟", errno);
exit(1);
}
/* Bind to a poer */
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, error = %d瀟", errno);
close(sfd);
exit(1);
}
/* make this a listen socket */
if(listen(sfd, 10) != 0){
LogError("listen() failed, error = %d瀟", errno);
close(sfd);
exit(1);
}
/*************************************************************/
/* Accept connection from clients. */
/*************************************************************/
while(1){/*while*/
if((cfd = accept(sfd, NULL, 0)) < 0){
LogError("accept() failed, error = %d瀟", errno);
close(sfd); exit(1);
}
printf("QSH_SERVER: accept OK.瀟");
/* Read the user profile and pasword from the client. */
/* the client send two null-terminated strings - */
/* the first one is the user profile and the second */
/* one is the password. */
if(GetString(cfd, profile, 11) != TRUE){
printf("QSH_SERVER: profile = [%s].瀟", profile);
getpeername(cfd, (struct sockaddr *)&client_addr, &client_addr_len);
LogError("Could not read profile from client at %s, port %hu瀟",
inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
close(cfd);
continue;
}
printf("QSH_SERVER: profile = [%s]瀟", profile);
if(GetString(cfd, password, 11) != TRUE){
printf("QSH_SERVER: password = [%s].瀟", password);
getpeername(cfd, (struct sockaddr *)&client_addr, &client_addr_len);
LogError("Could not read password from client at %s, port %hu瀟",
inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
close(cfd);
continue;
}
printf("QSH_SERVER: password = [%s]瀟", password);
/* Check for the special values that turns off passowrd 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瀟",
inet_ntoa(client_addr.sin_addr), client_addr.sin_port);
close(cfd);
continue;
}
/* QSYHETPH() requires tha the profile be exactly te charactors. */
/* left-aligned in the field, and padded with blanks. */
strtoupper(profile);
sprintf(sy_profile, "%-10.10s", profile);
printf("[%d] sy_profile = [%s]瀟", __LINE__, sy_profile);
sprintf(sy_password, "%-10.10s", password);
printf("[%d] sy_password = [%s]瀟", __LINE__, sy_password);
/* Get the profile handle for the client's user profile. */
QSYGETPH(sy_profile, sy_password, client_ph, &error, strlen(password), 0);
if(error.Bytes_Available != 0){/* ApiErr */
LogError("Could not get profile handle for profile %s.",
sy_profile, error.Exception_Id);
SendError(cfd, "Could not get profile handle for profile %s瀟",
sy_profile);
close(cfd);
continue;
}/* ApiErr */
printf("QSYGETPH success !瀟");
/* 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瀟",
sy_profile, error.Exception_Id);
SendError(cfd, "Could not get profile handle for profile %s瀟",
sy_profile);
close(cfd);
continue;
}
printf("QWTSETP success !瀟");
/* Get the info for this user profile */
if((cpw = getpwnam(profile)) == NULL){/* CPW 失敗 */
/* Log error */
LogError("Could not retrieve information for profile %s, "
"getpwnam() failed with errno=%d瀟",
profile, errno);
SendError(cfd, "Could not get profile handle for profile %s瀟",
sy_profile);
/* Switch back to the server's user profile */
QWTSETP(server_ph, &error);
if(error.Bytes_Available != 0){/* ApiErr */
LogError("Could not switch back to server's profile, "
"QWTSETP() failed with exception %7.7s瀟",
error.Exception_Id);
break;
}/* ApiErr */
/* Release this client's profile handle */
QSYRLSPH(client_ph, NULL);
if(error.Bytes_Available != 0){/* ApiErr */
LogError("Could not release client's profile handle, "
"QSYRLSPH() failed with exception %7.7s瀟",
error.Exception_Id);
break;
}/* ApiErr */
close(cfd);
continue;
}/* CPW 失敗 */
/* 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 inheritance structure */
memset(&inherit, '・', sizeof(struct inheritance));
inherit.flags = SPAWN_SETTHREAD_NP;
inherit.pgroup = SPAWN_NEWPGROUP;
/* Change to the home directory for the client.
The child process inherit 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 : %s瀟",
strerror(errno));
printf("errno = %d: %s瀟", errno, strerror(errno));
SendError(cfd, "Could not start qsh process by spawn error.瀟");
}
printf("spawn success !瀟");
/* Clean up for the next connection */
chdir(server_dir);
close(cfd);
printf("close cfd.瀟");
/* Switch back to server's user profile */
QWTSETP(server_ph, &error);
if(error.Bytes_Available != 0){/* ApiErr */
LogError("Could not switch back to server's profile, "
"QWTSETP() failed with exception %7.7s瀟",
error.Exception_Id);
break;
}/* ApiErr */
}/*while*/
/* Clean up */
printf("close sfd.瀟");
close(sfd);
#pragma disable_handler /* Exception handler */
#pragma disable_handler /* Cancel handler */
printf("* exit(0)瀟");
exit(0);
return(0);
/* Exception handler */
Cleanup:
LogError("Unexpected exception %7.7s瀟", ca.Msg_Id);
close(sfd);
exit(1);
}
/****************************/
int strtoupper(char *string)
/****************************/
{
for( ; *string != '・'; ++string){/*for-loop*/
*string = toupper(*string);
}/*for-loop*/
return 0;
}
/**************************************************/
int GetString(int fd, char *buffer, size_t nbytes)
/**************************************************/
{
char c;
do{
if(read(fd, &c, 1) != 1){/* read */
return FALSE;
}/* read */
*buffer ++ = c;
if(--nbytes == 0){
return TRUE;
}
} while(c != '・');
return TRUE;
}
/*******************************/
void LogError(char *format, ...)
/*******************************/
{
va_list ap;
time_t now; /* Time stamp */
printf("%s%s瀟", format, ap);
/* 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, format, ap);
va_end(ap);
/* Flush output to log file */
fflush(log_fp);
return;
}
/*****************************************/
void SendError(int fd, char *format, ...)
/*****************************************/
{
va_list ap;
/* Build the formatted string */
va_start(ap, format);
/*vsprintf(log_buffer, format, ap); */
sprintf(log_buffer, ">%s%s", format, ap);
/* Write the formatted string */
write(fd, log_buffer, strlen(log_buffer));
return;
}
/*************************************************/
void ClearHandler(_CNL_Hndlr_Parms_T *cancel_info)
/*************************************************/
{
int sfd;
sfd = *((int*)cancel_info->Com_Area);
close(sfd);
}

【QSH_CLIENT : サンプル・クライアント・プログラム】
/********************************************************************/
/* */
/* QSH_CLIENT : QSHELL セッション・クライアント */
/* */
/* [ 原典 ] QZSHSH IN IBM INFOMATION CENTER */
/* */
/* [ 記述 ] このプラグラムは SBMJOB された QSH_SERVER の */
/* クライアントとして最初は動作します。 */
/* 次に QSH_SERVER でユーザー認証を受けると */
/* QSH_SERVER は QZSHSH を SBMJOB します。 */
/* その後はこのプログラムは QZSHSH と対話して */
/* QZSHSH に SHELL コマンドを実行させて */
/* その結果を受け取ります。 */
/* */
/* [ パラメータ ] 1. Host running the qsh server (either host */
/* name or IP address) */
/* */
/* [ オプション ] 1. -N to force prompt for user name and */
/* password. */
/* 2. -P to specify port of qsh server. */
/* */
/* [ 注記 ] 1. IBM インフォーメーション・センターに公開されて */
/* いるサンプルは ASCII 用ですが、このプログラムは */
/* System i 上でコンパイル & 実行できるように */
/* 書き変えています。 */
/* 2. IBM インフォーメーション・センターに公開されて */
/* いるサンプルは */
/* extern int optind; */
/* extern char* optarg; */
/* が漏れているためにコンパイルはエラーとなりますが */
/* このプログラムでは補正しています。 */
/* 3. コンパイルは CRTBNDC でコンパイルしてください。 */
/* 4. 実行は CALL QSH_CLIENT PATM('-N' '-P 6042 ' 'HOST')*/
/* の形式で HOST には SYSTEM 名を指定してください。 */
/* */
/********************************************************************/
/* Remove the comments from the following line to use iconv() */
#define USE_ICONV 1
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h> /* socket(), bind(), ... */
#include <netinet/in.h> /* sockaddr_in */
#include <arpa/inet.h> /* inet_addr() */
#include <sys/ioctl.h> /* ioctl() */
#include <unistd.h> /* close(), read(), write() */
#include <errno.h>
#include <netdb.h> /* gethostbyname() */
#include <pwd.h> /* getpwuid() */
#include <signal.h> /* sigaction() */
#include <sys/types.h> /* select() */
#include <sys/time.h>
#ifdef AIX
#include <sys/select.h> /* select() */
#include <strings.h> /* bzero() for FD_ZERO */
#endif
#include <iconv.h> /* iconv() */
/*************************************************************/
/* 固 定 定 数 */
/*************************************************************/
#define TRUE 0
#define FALSE -1
#define QSH_PORT 6042
#define DEFAULT_BUF 4096
typedef unsigned char unchar;
/*************************************************************/
/* グ ロ ー バ ル 変 数 */
/*************************************************************/
char *sysname; /* Long host name of server system */
iconv_t ecd; /* Conversion descriptor for ASCII to EBCDIC */
iconv_t acd; /* Conversion descriptor for EBCDIC to ASCII */
/*************************************************************/
/* 内 部 使 用 関 数 */
/*************************************************************/
int GetPassword(char *, char *, char *);
void MySignalHandler(int);
void usage(void);
/********************************************************************/
/* m a i n --- main module of this pgm */
/* */
/* <PARAMETER> 1. ACCLOG */
/* */
/********************************************************************/
int main(int argc, char *argv[]){
struct sigaction sigact; /* Signal action */
int c; /* Option Letter */
int nflag=0; /* True when -n option is specified */
int port=QSH_PORT; /* Port to connect to on server */
int sd; /* Sockwet to server */
fd_set read_set; /* For select() */
int rc; /* Return code */
struct sockaddr_in svr_addr;/* AF_INET socket address */
long ip_addr; /* IP address of server system */
struct in_addr host_addr; /* Host address for gethostbyaddr() */
char* hostname; /* Short host name of server system */
size_t len; /* Length of input string */
char* ascii_user; /* Username in ASCII */
char* ebcdic_user; /* USername in EBCDIC */
char* ascii_pwd; /* Password in ASCII */
char* ebcdic_pwd; /* Password in EBCDIC */
struct hostent *host_p; /* Pointer to hostent structure returned
by gethostbyname() */
char* ascii_buf; /* Buffer of ASCII text */
char* ebcdic_buf; /* Buffer of EBCDIC text */
int buf_size; /* Amount of data read from server */
extern int optind;
extern char* optarg;
/*************************************************************/
/* Initialization */
/*************************************************************/
printf("**** QSH_CLIENT: SHELL クライアントの開始 ****瀟");
/* Set up signal handler for SIGINT. the signal is sent to the
process when the user presses <ctrl>c. */
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = MySignalHandler;
if(sigaction(SIGINT, &sigact, NULL) != 0){
perror("QSH_CLIENT:sogaction(SIGINT) faied.");
exit(1);
}
/*************************************************************/
/* Process the input parameters */
/*************************************************************/
if(argc < 2){
usage();
}
/* Process the options */
while((c = getopt(argc, argv, "HNP:")) != EOF){/* while */
switch(c){/*switch*/
case 'N':
nflag = 1;
break;
case 'P':
port = atoi(optarg);
break;
case 'H':
default:
usage();
break;
}/*switch*/
}/* while */
/* Convert a dotted decimal address to a 32-bit IP address */
hostname = argv[optind];
ip_addr = inet_addr(hostname);
/* When inet_addr() returns -1 assume the user specified
a host name */
if(ip_addr == -1){/* HOSTBYNAME */
/* Try to find the host by name */
host_p = gethostbyname(hostname);
if(host_p){/* HOST_P */
memcpy(&ip_addr, host_p->h_addr, host_p->h_length);
sysname = host_p->h_name;
}/* HOST_P */
else{/* ERR */
fprintf(stderr, "QSH_CLIENT: Could not find host %s瀟", hostname);
exit(1);
}/* ERR */
}/* HOSTBYNAME */
/* The user specified a IP address */
else{/* HOSTBYADDR */
/* Try to find the host by address */
host_addr.s_addr = ip_addr;
host_p = gethostbyaddr((char*)&host_addr.s_addr, sizeof(host_addr),
AF_INET);
if(host_p){/* HOST_P */
sysname = host_p->h_name;
}/* HOST_P */
else{/* ERR */
fprintf(stderr, "QSH_CLIENT:Could not find host %s瀟", hostname);
exit(1);
}/* ERR */
}/* HOSTBYADDR */
/*************************************************************/
/* Connect to the QSH_SERVER on the specified system */
/*************************************************************/
/* Create Socket */
if((sd = socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) < 0){
perror("QSH_CLIENT:socket() failed.");
exit(1);
}
/* Connect to the QSH_SERVER on specified system */
memset(&svr_addr, '・', sizeof(svr_addr));
svr_addr.sin_family = AF_INET;
svr_addr.sin_port = htons(port);
svr_addr.sin_addr.s_addr = ip_addr;
if(connect(sd, (struct sockaddr *)&svr_addr, sizeof(svr_addr)) != 0){
perror("QSH_CLIENT:connect() failed.");
exit(1);
}
printf("QSH_CLIENT: connect OK to QSH_SERVER.瀟");
/*************************************************************/
/* Send the user name and password to the server */
/*************************************************************/
printf("QSH_CLIENT: ユーザー名とパスワードの送信 瀟");
/* Allocate buffers for translating input and output */
ascii_buf = (char*)malloc(DEFAULT_BUF);
memset(ascii_buf, '・', DEFAULT_BUF);
ebcdic_buf = (char*)malloc(DEFAULT_BUF);
memset(ebcdic_buf, '・', DEFAULT_BUF);
ascii_user = ascii_buf;
ascii_pwd = ascii_buf + 100;
ebcdic_user = ebcdic_buf;
ebcdic_pwd = ebcdic_buf + 100;
/* Prompt the user for the user name and password */
if(nflag){
printf(" ユーザー名を入れてください。 瀟");
gets(ascii_user);
printf(" パスワードを入れてください。 瀟");
gets(ascii_pwd);
printf(" 瀟");
}
/* Convert the user name and password to EBCDIC */
strcpy(ebcdic_user, ascii_user);
strcpy(ebcdic_pwd, ascii_pwd);
/* Send the user name and password to the QSH_SEEVER.
Note the the user name and password are sent as plain text. */
if((rc = write(sd, (void*)ebcdic_user, strlen(ebcdic_user)+1)) < 0){
perror("QSH_CLIENT:write() fained sending username瀟");
close(sd);
exit(1);
}
if((rc = write(sd, (void*)ebcdic_pwd, strlen(ebcdic_pwd)+1)) < 0){
perror("QSH_CLIENT:write() fained sending password瀟");
close(sd);
exit(1);
}
printf("QSH_CLIENT: Started qsh session on %s瀟", sysname);
/********************************************************************/
/* Process input and output between the user and the remote shell */
/********************************************************************/
printf(" QSHELL コマンド入力 瀟");
/* Loopp forever */
while(1){/*while*/
/* Select on stdin and the socket connected to the remote shell */
FD_ZERO(&read_set);
FD_SET(0, &read_set);
FD_SET(sd, &read_set);
rc = select(sd+1, &read_set, NULL, NULL, NULL);
if((rc < 0) && (errno != EINTR)){
perror("QSH_CLIENT: select() failed.");
exit(1);
}
if(rc == 0) continue;
/* Process data enterd by the terminal user */
if(FD_ISSET(0, &read_set)){/* 端末からの読み取り */
/* Read the data from the terminal */
printf("Entry SHELL Cmd (END=QUIT)>瀟");
gets(ascii_buf);
if(strncmp(ascii_buf, "QUIT", 4) == 0) break;
/* Convert the string to EBCIDC */
len = strlen(ascii_buf);
memcpy(ebcdic_buf, ascii_buf, len);
/* Put a newline on the end of the string */
*(ebcdic_buf+len) = 0x25;
/* Send the data to the remote shell */
if(write(sd, ebcdic_buf, len+1) < 0){/* ERR */
perror("QSH_CLIENT:write() failed sending input.");
}/* ERR */
}/* 端末からの読み取り */
/* Process data from the remote shell */
if(FD_ISSET(sd, &read_set)){/* リモート SHELL */
/* Read the data from the remote shell */
buf_size = read(sd, ebcdic_buf, DEFAULT_BUF-1);
/* There was a failure reading from the remote shell */
if(buf_size < 0){/* ERR */
perror("瀟QSH_CLIENT: error reading from remote shell");
printf("%s のセッションが終了した。 瀟", sysname);
exit(0);
}/* ERR */
/* The remote shell process ended */
else if(buf_size == 0){/* ゼロ */
printf("%s のセッションが終了した。 瀟", sysname);
exit(0);
}/* ゼロ */
/* Process the data from the remote shell */
else{/* 正 */
/* Convert to ASCII */
*(ebcdic_buf+buf_size) = '・';
if((int)buf_size >= 0){/* サイズ */
memcpy(ascii_buf, ebcdic_buf, (int)buf_size);
/* write(1, ascii_buf, buf_size); 標準出力 */
printf("%s瀟", ascii_buf);
}/* サイズ */
}/* 正 */
}/* リモート SHELL */
}/*while*/
exit(0);
}
/*************************************************************/
int GetPassword(char *sysname, char *logname, char *password)
/*************************************************************/
{
#define BUFSIZE 256
char buffer[BUFSIZE];
char *systag, *logtag;
int logflag = 0, pwdflag = 0;
FILE *netrc;
struct passwd *pwdbuf;
int rc = 0;
/* Get user's home directory */
pwdbuf = getpwuid(getuid());
/* Does user have a netrc file in their home directory ? */
strcat(strcpy(buffer, pwdbuf->pw_dir), "/.netrc");
if((netrc = fopen(buffer, "r")) == NULL){
perror("QSH_CLIENT: open() failed.");
return FALSE;
}
while(!(logflag || pwdflag) && fgets(buffer, BUFSIZE, netrc)!= NULL){/*while*/
/* Find system name in =/.netrc */
if((systag = (char*)strtok(buffer, " 炙瀟")) != NULL &&
!strncmp(systag, "machine", 7)){
systag = (char*)strtok(NULL, " 炙瀟");
if(!strcmp(systag, sysname)){
/* Find login and password */
while(!logtag || !pwdflag){/*while-2*/
if((logtag = (char*)strtok(NULL, " 炙瀟")) == NULL){
while(!logtag){/*while-3*/
fgets(buffer, BUFSIZE, netrc);
logtag = (char*)strtok(buffer, " 炙瀟");
}/*while-3*/
}
if(!strncmp(logtag, "login", 5)){/* not login */
strcpy(logname, strtok(NULL, " 瀟炙"));
++ logflag;
}/* not login */
else if(!strncmp(logtag, "password", 8)){
strcpy(password, strtok(NULL, " 瀟炙"));
++ pwdflag;
}
else ;
}/*while-2*/
}
}
}/*while*/
fclose(netrc);
/* Login and password not found for system */
if(!(logflag && pwdflag)){
rc = FALSE;
}
return rc;
}
/******************************/
void MySignalHandler(int signo)
/******************************/
{
switch(signo){/*switch*/
case SIGINT:
printf("瀟QSG_CLIENT: <ctrl> c=ends this program瀟");
printf("Ended qsh session on %s瀟", sysname);
exit(0);
break;
default:
exit(1);
break;
}/*switch*/
return;
}
/***************/
void usage(void)
/***************/
{
fprintf(stderr,
" 呼び出しは CALL QSH_CLIENT PARM('-N' '-P 6042' HOSTNAME瀟");
exit(-1);
}

■ サンプル・プログラムの実行方法
-
@ サーバー・プログラムの投入
- SBMJOB CMD(CALL PGM(MYLIB/QSH_SERVER)) JOB(QSH_SERVER) + [実行] によって
QSH_SERVER をバッチ・ジョブに投入する。
QSH_SERVER はサブ・システム QBATCH の配下で TIMW で次のように待機する。

-
A クライアント・プログラムの実行
- CALL PGM(TEST.COM/QSH_CLIENT) PARM(‘-N’ ‘-P 6042’ Sxxxxxxx) + [実行]によって
(但し Sxxxxxxx は System i のシステム名)
– 実行は JOBCCSID=5035, ホスト・コード・ページ= 939 の環境で行うこと。
IBM のサンプル・ソースではパラメータは -n のように英小文字であったが
日本語環境でも使用できるように英大文字に変更した。

-
B ユーザー名とパスワードを入力
- 次のようにユーザー名とパスワードを入力して認証すると SHELL コマンドの入力が
可能となる。

-
C SHELL コマンドを入力する。
- ディレクトリーを表示する LS コマンドーや現在のパスを示す PWD コマンドを
入力して実行を確認する。

-
D QUIT を入力してクライアント・プログラムを終了する。
- コマンド欄に文字列「QUIT」を入力して実行すると
クライアント・プログラムを終了させることができる。

■ QSHELLサンプル・ソースが示している処理構造
さて、QSHELL の実行方法は、いくつもあって STRQSH, QSH , … などのコマンドで QSHELL を開始することが
できる。いずれの場合でも QSHELL/QZSHSH というプログラムがバッチ・ジョブとして投入されて
STRQSH 等の SHELL環境クライアント・プログラムは、この QZSHSH というプログラムと プロセス間通信で
通信して結果を表示しているのである。例えば STRQSH コマンドを実行してから WRKACTJOB で見ると
どこかで QZSHSH というプログラムが必ずバッチで起動されているはずであるので是非、確かめて頂きたい。
この処理構造は次のような関係である。

【解説】
最初に@ユーザー認証をサーバー・プログラム QSH_SERVER に要求して、QSH_SERVER は
SHELLコマンドを実行するための QZSHSH プログラムをAバッチ投入する。
その後は QSH_SERVER の手を離れて クライアント: QSH_CLIENT は直接、QZSHSH と
SHELLコマンドの実行を依頼して結果を得る。
つまり QSH_SERVER は SHELL コマンドの実行のための待機サーバーの役割を果たしている。
同時に QZSHSH に投入する spawn 関数を解析すれば QZSHSH はどのようなパラメータや
環境が必要であるのかを知ることができる。
このサンプルはクライアント環境を GUI 化または Web化するための方法を示唆している。
事実、IBM はコメントの中で、これらのサンプルを使って製品を作ることも良しと謳っている。
