/* -------------------- SAS/C Sample Program -------------------- * * MODULE: rexec.c - sample rexec() function implementation * */ #include #include #include #include #include #include #include #ifdef __SASC__ static char *strcpy_to_ascii(char *ascii, char *ebcdic); #endif static int fail(const char *msg, int primary, int secondary); /* * NAME: rexec() * PARMS: -Name----------type-----Description---------------------- * host char ** Host name string pointer - passed * by reference * port int port for remote execution * name char * Username on remote host * passwd char * Password on remote host * cmd char * Command to execute * ptr_secondary int * stderr socket descriptor area ptr * or NULL * RETURN:-Name----------type-----Description---------------------- * int primary socket or -1 for failure */ int rexec(char **host, int port, char *name, char *passwd, char *cmd, int *ptr_secondary) { /* Internet address format of socket address structure. */ struct sockaddr_in sa, sa2, server_sa; struct hostent *hp; /* Results of host lookup. */ int primary; /* Primary connection socket. */ int secondary; /* Secondary connection socket. */ int secondary_model; /* Model socket used for bind/accept. */ char portstr[6]; /* string for decimal port number */ int len, rc, n; char c; #ifdef __SASC__ /* * The protocol expects the username, password and * command string in ASCII. For callers on EBCDIC hosts, * this routine translates the parameters to ASCII. */ #define CMDMAX 1024 #define NAMEMAX 80 #define PASSMAX 80 char name_ascii[NAMEMAX]; char passwd_ascii[PASSMAX]; char cmd_ascii[CMDMAX]; strcpy_to_ascii(name_ascii, name); name = name_ascii; strcpy_to_ascii(passwd_ascii, passwd); passwd = passwd_ascii; strcpy_to_ascii(cmd_ascii, cmd); cmd = cmd_ascii; #endif /* * Lookup host and extract address. */ hp = gethostbyname(*host); if (hp==NULL) { herror(*host); /* Print msg describing error. */ return -1; } *host = hp->h_name; /* * Create primary connection socket using TCP. */ primary = socket(AF_INET, SOCK_STREAM, 0); if (primary < 0) return fail("socket() failed", -1, -1); /* * Fill in address of server and connect. */ memset(&sa, '\0', sizeof(sa)); sa.sin_family = AF_INET; /* Internet-type address. */ memcpy(&sa.sin_addr, hp->h_addr, hp->h_length); /* IP address. */ sa.sin_port = port; /* TCP port. */ if (connect(primary, &sa, sizeof(sa))<0) /* Likely to be "connection refused".Show host name in */ /* message. */ return fail(*host, primary, -1); /* * Create secondary connection (if requested) */ if (ptr_secondary == NULL) { /* Write one byte of zero to indicate no additional */ /* connection desired. */ write(primary, "", 1); secondary = -1; } else { /* * Create model socket for secondary connection. */ secondary_model = socket(AF_INET, SOCK_STREAM, 0); if (secondary_model <0) return fail("socket() failed", primary, -1); /* * Bind it to a transient port. * Leaving local address as zero allows all IP * addresses for the machine to be used * Leaving the port zero allows TCP to select a * transient port. * Set up inconming connection queue (using listen()). */ memset(&sa2, '\0', sizeof(sa2)); sa2.sin_family = AF_INET; if (bind(secondary_model, &sa2, sizeof(sa2))<0) return fail("bind() failed", primary, secondary_model); if (listen(secondary_model, 1)<0) return fail("listen() failed", primary, secondary_model); /* * Get port number and send it to the rexec server. */ len = sizeof(sa2); rc = getsockname(secondary_model, &sa2, &len); if (rc < 0 || len < sizeof(sa2)) return fail("getsockname() failed", primary, secondary_model); /* The port is in network byte order within the */ /* "struct sockaddr_in". We need it in local */ /* byte order for sprintf to interpret. */ sprintf(portstr, "%u", ntohs(sa2.sin_port)); #ifdef __SASC__ strcpy_to_ascii(portstr, portstr); /* Server needs ASCII */ #endif if (write(primary, portstr, strlen(portstr)+1)<0) return fail("write() failed",primary,secondary_model); /* * Await an incoming connection request. accept() will * return the actual socket to use for stderr. */ len = sizeof(server_sa); secondary = accept(secondary_model, &server_sa, &len); close(secondary_model); if (secondary < 0 || len < sizeof(server_sa)) return fail("accept() failed",primary,-1); *ptr_secondary = secondary; /* Provide to caller. */ } /* * Write username, password and command to the server. In * each case, the terminating '\0' is sent as a delimiter. */ rc = write(primary, name, strlen(name)+1); if (rc < 0) return fail("write() failed",primary,secondary); rc = write(primary, passwd, strlen(passwd)+1); if (rc < 0) return fail("write() failed",primary,secondary); rc = write(primary, cmd, strlen(cmd)+1); if (rc < 0) return fail("write() failed",primary,secondary); /* * Server should send a single '\0' to indicate success. * Otherwise it should send a single byte of binary 0x1 * to indicate failure, followed by a newline-terminated * ASCII error message. */ if (read(primary, &c, 1) != 1) return fail("read() failed",primary, secondary); if (c!='\0') /* Server error message. */ { do { n = read(primary, &c, 1); if (n==0) break; /* Really expected '\n', but EOF is good enough. */ if (n<0) return fail("read() failed", primary, secondary); #ifdef __SASC__ c = ntohcs(c); /* Msg needs to print in EBCDIC. */ #endif fputc(c, stderr); } while (c!='\n'); return -1; } /* * The caller is now responsible for handling traffic over * the primary and (if requested) secondary connections. */ return primary; } /* * Clean-up logic. */ static int fail(const char *msg, int primary, int secondary) { perror(msg); if (primary>=0) close(primary); if (secondary>=0) close(secondary); return -1; } #ifdef __SASC__ /* Need conversion to ASCII */ static char *strcpy_to_ascii(char *ascii, char *ebcdic) { int i; /* * htoncs() is a SAS/C specific function which converts * a character from ASCII to EBCDIC. */ for (i=0; ebcdic[i]!='\0'; i++) ascii[i] = htoncs(ebcdic[i]); ascii[i] = '\0'; return ascii; } #endif