struct sio_hdl * fdpass_sio_open(int num, unsigned int mode) { int fd; if (!fdpass_send(fdpass_peer, FDPASS_OPEN_SND, num, mode, -1)) return NULL; if (!fdpass_waitret(fdpass_peer, &fd)) return NULL; if (fd < 0) return NULL; return sio_sun_fdopen(fd, mode, 1); }
struct mio_hdl * fdpass_mio_open(int num, unsigned int mode) { int fd; if (!fdpass_send(fdpass_peer, FDPASS_OPEN_MIDI, num, mode, -1)) return NULL; if (!fdpass_waitret(fdpass_peer, &fd)) return NULL; if (fd < 0) return NULL; return mio_rmidi_fdopen(fd, mode, 1); }
void fdpass_in_helper(void *arg) { int cmd, num, mode, fd; struct fdpass *f = arg; struct dev *d; struct port *p; if (!fdpass_recv(f, &cmd, &num, &mode, &fd)) return; switch (cmd) { case FDPASS_OPEN_SND: d = dev_bynum(num); if (d == NULL || !(mode & (SIO_PLAY | SIO_REC))) { if (log_level >= 1) { fdpass_log(f); log_puts(": bad audio device or mode\n"); } fdpass_close(f); return; } fd = sio_sun_getfd(d->path, mode, 1); break; case FDPASS_OPEN_MIDI: p = port_bynum(num); if (p == NULL || !(mode & (MIO_IN | MIO_OUT))) { if (log_level >= 1) { fdpass_log(f); log_puts(": bad midi port or mode\n"); } fdpass_close(f); return; } fd = mio_rmidi_getfd(p->path, mode, 1); break; default: fdpass_close(f); return; } fdpass_send(f, FDPASS_RETURN, 0, 0, fd); }
// we launch the job via a wrapper. the full chain of exec() calls looks // like: // // (condor_starter)->(condor_glexec_run)->(glexec)->(condor_glexec_wrapper)->(job) // // glexec_wrapper serves two purposes: // - it allows us to pass environment variables to the job, which glexec // (as of 08/2008) does not support // - it allows us to distinguish between failures in the job and failures // in condor_glexec_run or glexec or condor_glexec_wrapper // // this function: // - sends the job's environment over to the wrapper's stdin (which is a // UNIX domain socket we set up to communicate with the wrapper) // - sends the job's stdin FD to the wrapper // - waits for an error message from the wrapper; if EOF is read first, the // job was successfully exec()'d // int GLExecPrivSepHelper::feed_wrapper(int pid, int sock_fds[2], Env& env, int dc_job_opts, int job_std_fds[3], int &glexec_err_fd, MyString *error_msg, int *glexec_rc) { // we can now close the end of the socket that we handed down // to the wrapper; the other end we'll use to send stuff over // close(sock_fds[1]); // if pid is 0, Create_Process failed; just close the socket // and return // if (pid == FALSE) { close(sock_fds[0]); return pid; } unsigned int hello = 0; ssize_t bytes = full_read(sock_fds[0], &hello, sizeof(int)); if (bytes != sizeof(int)) { dprintf(D_ALWAYS, "GLEXEC: error reading hello from glexec_job_wrapper\n"); close(sock_fds[0]); if( bytes <= 0 ) { // Since we failed to read the expected hello bytes // from the wrapper, this likely indicates that glexec // failed to execute the wrapper. Attempt to read an // error message from glexec. MyString glexec_stderr; FILE *fp = fdopen(glexec_err_fd,"r"); if( fp ) { while( glexec_stderr.readLine(fp,true) ); fclose(fp); glexec_err_fd = -1; // fd is closed now } // Collect the exit status from glexec. Since we // created this process via // DaemonCore::Create_Process() we could/should wait // for DaemonCore to reap the process. However, given // the way this function is used in the starter, that // happens too late. int glexec_status = 0; if (waitpid(pid,&glexec_status,0)==pid) { if (WIFEXITED(glexec_status)) { int status = WEXITSTATUS(glexec_status); ASSERT( glexec_rc ); *glexec_rc = status; dprintf(D_ALWAYS, "GLEXEC: glexec call exited with status %d\n", status); if( error_msg ) { error_msg->formatstr_cat( " glexec call exited with status %d", status); } } else if (WIFSIGNALED(glexec_status)) { int sig = WTERMSIG(glexec_status); dprintf(D_ALWAYS, "GLEXEC: glexec call exited via signal %d\n", sig); if( error_msg ) { error_msg->formatstr_cat( " glexec call exited via signal %d", sig); } } } if( !glexec_stderr.IsEmpty() ) { glexec_stderr.trim(); StringList lines(glexec_stderr.Value(),"\n"); lines.rewind(); char const *line; if( error_msg ) { *error_msg += " and with error output ("; } int line_count=0; while( (line=lines.next()) ) { // strip out the annoying line about pthread_mutex_init if( strstr(line,"It appears that the value of pthread_mutex_init") ) { continue; } line_count++; if( !glexec_stderr.IsEmpty() ) { glexec_stderr += "; "; } dprintf(D_ALWAYS, "GLEXEC: error output: %s\n",line); if( error_msg ) { if( line_count>1 ) { *error_msg += "; "; } *error_msg += line; } } if( error_msg ) { *error_msg += ")"; } } } errno = 0; // avoid higher-level code thinking there was a syscall error return FALSE; } if( hello != 0xdeadbeef ) { dprintf(D_ALWAYS, "GLEXEC: did not receive expected hello from wrapper: %x\n", hello); close(sock_fds[0]); return FALSE; } // now send over the environment // Env env_to_send; if (HAS_DCJOBOPT_ENV_INHERIT(dc_job_opts)) { env_to_send.MergeFrom(environ); } env_to_send.MergeFrom(env); MyString env_str; MyString merge_err; if (!env_to_send.getDelimitedStringV2Raw(&env_str, &merge_err)) { dprintf(D_ALWAYS, "GLEXEC: Env::getDelimitedStringV2Raw error: %s\n", merge_err.Value()); close(sock_fds[0]); return FALSE; } const char* env_buf = env_str.Value(); int env_len = env_str.Length() + 1; errno = 0; if (full_write(sock_fds[0], &env_len, sizeof(env_len)) != sizeof(env_len)) { dprintf(D_ALWAYS, "GLEXEC: error sending env size to wrapper: %s\n", strerror(errno)); close(sock_fds[0]); return FALSE; } errno = 0; if (full_write(sock_fds[0], env_buf, env_len) != env_len) { dprintf(D_ALWAYS, "GLEXEC: error sending env to wrapper: %s\n", strerror(errno)); close(sock_fds[0]); return FALSE; } // now send over the FDs that the Starter should use for stdin/out/err // int i; for(i=0;i<3;i++) { int std_fd = job_std_fds[i]; if (std_fd != -1) { int pipe_fd; if (daemonCore->Get_Pipe_FD(std_fd, &pipe_fd) == TRUE) { std_fd = pipe_fd; } if (fdpass_send(sock_fds[0], std_fd) == -1) { dprintf(D_ALWAYS, "GLEXEC: fdpass_send failed\n"); close(sock_fds[0]); return FALSE; } } } // Now we do a little dance to replace the socketpair that we // have been using to communicate with the wrapper with a new // one. Why? Because, as of glexec 0.8, when glexec is // configured with linger=on, some persistent process // (glexec?, procd?) is keeping a handle to the wrapper's end // of the socket open, so the starter hangs waiting for the // socket to close when the job is executed. int old_sock_fd = sock_fds[0]; if (socketpair(PF_UNIX, SOCK_STREAM, 0, sock_fds) == -1) { dprintf(D_ALWAYS, "GLEXEC: socketpair error: %s\n", strerror(errno)); close(old_sock_fd); return FALSE; } if (fdpass_send(old_sock_fd, sock_fds[1]) == -1) { dprintf(D_ALWAYS, "GLEXEC: fdpass_send failed on new sock fd\n"); close(old_sock_fd); close(sock_fds[0]); close(sock_fds[1]); return FALSE; } // close our handle to the wrapper's end of the socket close(sock_fds[1]); // close our old socket close(old_sock_fd); // now read any error messages produced by the wrapper // char err[256]; bytes = full_read(sock_fds[0], err, sizeof(err) - 1); if (bytes == -1) { dprintf(D_ALWAYS, "GLEXEC: error reading message from wrapper: %s\n", strerror(errno)); close(sock_fds[0]); return FALSE; } if (bytes > 0) { err[bytes] = '\0'; dprintf(D_ALWAYS, "GLEXEC: error from wrapper: %s\n", err); if( error_msg ) { error_msg->formatstr_cat("glexec_job_wrapper error: %s", err); } // prevent higher-level code from thinking this was a syscall error errno = 0; return FALSE; } // if we're here, it all worked // close(sock_fds[0]); return pid; }
bool glexec_starter_handle_env(pid_t pid) { // we can now close the end of the socket that we handed down // to the wrapper; the other end we'll use to send stuff over // close(s_saved_sock_fds[1]); int sock_fd = s_saved_sock_fds[0]; // if pid is 0, Create_Process failed; just close the socket // and return // if (pid == 0) { close(sock_fd); return false; } // before sending the environment, scrub some stuff that was // only for glexec // s_saved_env.SetEnv("GLEXEC_MODE", ""); s_saved_env.SetEnv("GLEXEC_CLIENT_CERT", ""); s_saved_env.SetEnv("GLEXEC_SOURCE_PROXY", ""); s_saved_env.SetEnv("GLEXEC_TARGET_PROXY", ""); // now send over the environment; what we send over is our own // environment with the stuff we added in prepareForGlexec // added in // Env env_to_send; env_to_send.MergeFrom(environ); env_to_send.MergeFrom(s_saved_env); MyString env_str; MyString merge_err; if (!env_to_send.getDelimitedStringV2Raw(&env_str, &merge_err)) { dprintf(D_ALWAYS, "GLEXEC: Env::getDelimitedStringV2Raw error: %s\n", merge_err.Value()); close(sock_fd); return false; } const char* env_buf = env_str.Value(); int env_len = env_str.Length() + 1; errno = 0; if (write(sock_fd, &env_len, sizeof(env_len)) != sizeof(env_len)) { dprintf(D_ALWAYS, "GLEXEC: error sending env size to wrapper: %s\n", strerror(errno)); close(sock_fd); return false; } errno = 0; if (write(sock_fd, env_buf, env_len) != env_len) { dprintf(D_ALWAYS, "GLEXEC: error sending env to wrapper: %s\n", strerror(errno)); close(sock_fd); return false; } // now send over the FD that the Starter should use for stdin, if any // (we send a flag whether there is an FD to send first) // int flag = (s_saved_starter_stdin != -1) ? 1 : 0; if (write(sock_fd, &flag, sizeof(flag)) != sizeof(flag)) { dprintf(D_ALWAYS, "GLEXEC: error sending flag to wrapper: %s\n", strerror(errno)); close(sock_fd); return false; } if (flag) { int fd; int rv = daemonCore->Get_Pipe_FD(s_saved_starter_stdin, &fd); if (rv == FALSE) { fd = s_saved_starter_stdin; } if (fdpass_send(sock_fd, fd) == -1) { dprintf(D_ALWAYS, "GLEXEC: fdpass_send failed\n"); close(sock_fd); return false; } } // now read any error messages produced by the wrapper // char err[256]; ssize_t bytes = read(sock_fd, err, sizeof(err) - 1); if (bytes == -1) { dprintf(D_ALWAYS, "GLEXEC: error reading message from wrapper: %s\n", strerror(errno)); close(sock_fd); return false; } if (bytes > 0) { err[bytes] = '\0'; dprintf(D_ALWAYS, "GLEXEC: error from wrapper: %s\n", err); return false; } // if we're here, it all worked // close(sock_fd); return true; }