#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern char **environ; #define LIBDIR "/ROCK/tools.cross/pseudonative.lib" #define BINDIR_1 "/ROCK/tools.cross/pseudonative.bin" #define BINDIR_2 "/ROCK/tools.cross/bin" #define BINDIR_SIZE 100 #define TCPPORT 28336 #define CMD_EXEC 1 #define CMD_ADDARG 2 #define CMD_ADDENV 3 #define CMD_CHDIR 4 #define CMD_CHROOT 5 #define CMD_EXIT 6 #define CMD_BUILDID 7 #define CMD_WRITE0 1000 #define CMD_CLOSE0 2000 #define CMD_ENABLE0 3000 int config_debug = 0; int config_remote = 0; struct pn_pkg { uint32_t len; uint32_t type; char data[]; }; int conn_open(const char *peer) { struct sockaddr_in sin; int conn; sin.sin_family = PF_INET; sin.sin_port = htons(TCPPORT); if ( !inet_aton(peer, &sin.sin_addr) ) { fprintf(stderr, "pseudonative_handler: can't parse address '%s'\n", peer); exit(-1); } conn = socket(PF_INET, SOCK_STREAM, 0); if (conn < 0) { fprintf(stderr, "pseudonative_handler: can't create socket: %s\n", strerror(errno)); exit(-1); } if (connect(conn, (struct sockaddr *)&sin, sizeof (sin)) < 0) { fprintf(stderr, "pseudonative_handler: can't connect to remote host: %s\n", strerror(errno)); exit(-1); } return conn; } void conn_send(int conn, int len, int type, char *data) { struct pn_pkg *p = malloc( sizeof(struct pn_pkg) + len ); int i, rc; p->len = htonl(len); p->type = htonl(type); memcpy(p->data, data, len); len += sizeof(struct pn_pkg); for (i = 0; i < len; i += rc) { rc = write(conn, (char*)p + i, len - i); if ( rc <= 0 ) { if ( errno == ECONNRESET && type == CMD_CLOSE0 ) break; fprintf(stderr, "pseudonative_handler: network write error: %s\n", strerror(errno)); exit(-1); } } free(p); } int conn_read(int conn, char *buf, int len, int nonblock) { int f = -1; if ( nonblock ) { f = fcntl(conn, F_GETFL, 0); fcntl(conn, F_SETFL, f | O_NONBLOCK); } while (len) { int rc = read(conn, buf, len); if ( rc < 0 ) { if ( errno == EAGAIN ) break; fprintf(stderr, "pseudonative_handler: network read error: %s\n", strerror(errno)); exit(-1); } if ( rc == 0 ) { fprintf(stderr, "pseudonative_handler: connection closed by peer\n"); exit(-1); } if ( f != -1 ) fcntl(conn, F_SETFL, f); len -= rc; buf += rc; f = -1; } if ( f != -1 ) fcntl(conn, F_SETFL, f); return len; } struct pn_pkg *conn_recv(int conn) { struct pn_pkg *p = malloc( sizeof(struct pn_pkg) ); if ( conn_read(conn, (char*)p, 8, 1) ) { free(p); return 0; } p->len = ntohl(p->len); p->type = ntohl(p->type); p = realloc(p, sizeof(struct pn_pkg) + p->len); conn_read(conn, p->data, p->len, 0); return p; } int exec_remote(char ** argv) { char buf[1024], *txt; int ret = -1, open_0 = 1; int enable_0 = 0; int f, i, conn, rc; struct timeval tv; struct pn_pkg *p; fd_set rfds; /* connect */ txt = getenv("ROCKCFG_PSEUDONATIVE_NATIVEHOST"); if ( txt && *txt ) { conn = conn_open(txt); } else { fprintf(stderr, "pseudonative_handler: no native host configured\n"); exit(-1); } /* send build id */ txt = getenv("ROCKCFG_ID"); if ( txt && *txt ) conn_send(conn, strlen(txt)+1, CMD_BUILDID, txt); /* mount and chroot */ txt = getenv("ROCKCFG_PSEUDONATIVE_NFSROOT"); if ( txt && *txt ) conn_send(conn, strlen(txt)+1, CMD_CHROOT, txt); /* current directory */ if ( getcwd(buf, 1024) ) conn_send(conn, strlen(buf)+1, CMD_CHDIR, buf); /* send environment */ for (i=0; environ[i]; i++) conn_send(conn, strlen(environ[i])+1, CMD_ADDENV, environ[i]); /* send arguments */ for (i=0; argv[i]; i++) conn_send(conn, strlen(argv[i])+1, CMD_ADDARG, argv[i]); /* execute command */ conn_send(conn, strlen(argv[0])+1, CMD_EXEC, argv[0]); /* we pass sigpipe's to peer */ signal(SIGPIPE, SIG_IGN); while (1) { FD_ZERO(&rfds); if ( open_0 && enable_0 ) FD_SET(0, &rfds); FD_SET(conn, &rfds); nextselect: tv.tv_sec = 1; tv.tv_usec = 0; rc = select(conn+1, &rfds, 0, 0, &tv); if ( rc < 0 ) { if ( errno == EINTR ) goto nextselect; fprintf(stderr, "pseudonative_handler: select error: %s\n", strerror(errno)); exit(-1); } nextread: if ( (p = conn_recv(conn)) != 0 ) { char *d = p->data; switch (p->type) { case CMD_WRITE0+1: while ( p->len ) { rc = write(1, d, p->len); if ( rc <= 0 ) { if ( errno == EPIPE ) { conn_send(conn, 0, CMD_CLOSE0+1, 0); break; } fprintf(stderr, "pseudonative_handler: write error on fd1: %s\n", strerror(errno)); exit(-1); } d += rc; p->len -= rc; } break; case CMD_WRITE0+2: while ( p->len ) { rc = write(2, d, p->len); if ( rc <= 0 ) { if ( errno == EPIPE ) { conn_send(conn, 0, CMD_CLOSE0+2, 0); break; } fprintf(stderr, "pseudonative_handler: write error on fd2: %s\n", strerror(errno)); exit(-1); } d += rc; p->len -= rc; } break; case CMD_CLOSE0: close(0); open_0 = 0; break; case CMD_CLOSE0+1: close(1); break; case CMD_CLOSE0+2: close(2); break; case CMD_ENABLE0: enable_0 = 1; break; case CMD_EXIT: ret = (unsigned char)p->data[0]; free(p); goto finish; } free(p); goto nextread; } if( open_0 && enable_0 ) { f = fcntl(0, F_GETFL, 0); fcntl(0, F_SETFL, f | O_NONBLOCK); rc = read(0, buf, 512); fcntl(0, F_SETFL, f); if ( rc > 0 ) { conn_send(conn, rc, CMD_WRITE0, buf); goto nextread; } else if (rc == 0) { conn_send(conn, 0, CMD_CLOSE0, 0); open_0 = 0; } else if ( errno != EAGAIN ) { perror("Error on read from f2:"); } } } finish: close(conn); if (ret == -1) fprintf(stderr, "pseudonative_handler: sometimes everything goes wrong\n"); return ret; } void set_ld_lib_path() { FILE *f = fopen("/etc/ld.so.conf", "r"); char ld_lib_path[1024] = "/lib"; int written = 4, len, i; char line[1024], *l; glob_t globbuf; l = getenv("LD_LIBRARY_PATH_PSEUDONATIVE_BACKUP"); if (l) { l = strdup(l); l = strtok(l, ":"); while ( l ) { if ( strcmp(l, LIBDIR) && written < 1024) written += snprintf(ld_lib_path+written, 1024-written, ":%s", l); l = strtok(0, ":"); } unsetenv("LD_LIBRARY_PATH_PSEUDONATIVE_BACKUP"); } if (f) { while ( (l=fgets(line, 1024, f)) ) { while ( *l == ' ' || *l == '\t' || *l == '\n' ) l++; if ( *l == '#' || !*l ) continue; len = strcspn(l, "\t\n "); if (len) { l[len] = 0; glob(l, GLOB_ONLYDIR, 0, &globbuf); for (i=0; i 1 ) { if ( !strcmp(argv[1], "-d") ) config_debug = 1; else if ( !strcmp(argv[1], "-r") ) config_remote = 1; else { fprintf(stderr, "pseudonative_handler: unknown option %s.\n", argv[1]); return -1; } argv++; argc--; } if (argc < 2) { fprintf(stderr, "pseudonative_handler: arguments missing.\n"); return -1; } cmd = basename(argv[1]); mycmd = malloc(BINDIR_SIZE+strlen(cmd)+10); t = getenv("LD_LIBRARY_PATH"); if ( t && strcmp(t, LIBDIR) ) setenv("LD_LIBRARY_PATH_PSEUDONATIVE_BACKUP", t, 1); setenv("LD_LIBRARY_PATH", LIBDIR, 1); /* The target ld.so.cache would confuse bins for the build host. So * we unlink it here and generate a LD_LIBRARY_PATH from ld.so.conf * for bins executed on the remote machine. */ unlink("/etc/ld.so.cache"); sprintf(mycmd, "%s/%s", BINDIR_1, cmd); if ( !access(mycmd, X_OK) && !config_remote ) { if (config_debug) fprintf(stderr, "pseudonative_handler: local: %s\n", mycmd); argv[1] = mycmd; write_log("local", argv+1); execv(mycmd, argv+1); goto goterror; } sprintf(mycmd, "%s/%s", BINDIR_2, cmd); if ( !access(mycmd, X_OK) && !config_remote ) { if (config_debug) fprintf(stderr, "pseudonative_handler: local: %s\n", mycmd); argv[1] = mycmd; write_log("local", argv+1); execv(mycmd, argv+1); goto goterror; } /* FIXME: CMD_ENABLE0 doesn't work correctly yet (see pseudonative_daemon.c). * So we close fd0 explicitely for some programs here. */ #if 0 if ( !strcmp(cmd, "tput") ) { close(0); open("/dev/null", O_RDONLY); } #endif set_ld_lib_path(); if (config_debug) fprintf(stderr, "pseudonative_handler: remote: %s\n", argv[1]); write_log("remote", argv+1); return exec_remote(argv+1); goterror: fprintf(stderr, "pseudonative_handler: error calling '%s': %s\n", mycmd, strerror(errno)); return -1; }