/**********************************************************************/ /* */ /* 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.