// Apply single quotes to prevent shell from evaluating special chars char *quote_for_remote(const char *arg) { const char *p; int c; for (p=arg, c=0; NULL != (p = strchr(p, '\'')); ++c, ++p) { /*empty*/ } size_t old_len = strlen(arg); size_t new_len = old_len + 2 + 3 * c; char *result = (char *)AMUDP_malloc(1 + new_len); result[0] = '\''; result[1] = '\0'; char *end = result+1; if (c) { char *dup = (char *)AMUDP_malloc(1 + old_len); p = strcpy(dup, arg); while (c--) { char *q = strchr((char *)p, '\''); *q = '\0'; strcpy(end, p); end += q-p; strcpy(end, "'\\''"); end += 4; p = q + 1; } AMUDP_free(dup); } else { p = arg; } strcpy(end, p); AMUDP_assert(strlen(result) == new_len-1); result[new_len-1] = '\''; result[new_len] = '\0'; return result; }
// Apply quotes to protect against the shell spawned by system() // Note this is for use *inside* the double quotes we place around a command run by system. // shell docs tells us: // Inside double-quotes, '$' and '`' remain special unconditionally, // and '\' remains special only if followed by another special char *quote_for_local(const char *arg) { const char specials[] = "$`\\\""; char *tmp = quote_for_remote(arg); // count specials (assume every backslash is special) const char *p; int c; for (p=tmp, c=0; NULL != (p = strpbrk(p, specials)); ++c, ++p) { /*empty*/ } if (!c) return tmp; // Nothing to protect from local shell size_t old_len = strlen(tmp); size_t new_len = old_len + c; char *result = (char *)AMUDP_malloc(1 + new_len); char *end = result; if (c) { char *q, *dup = (char *)AMUDP_malloc(1 + old_len); p = strcpy(dup, tmp); while (NULL != (q = strpbrk((char *)p, specials))) { size_t len = q-p; strncpy(end, p, len); end += len; end[0] = '\\'; end += 1; if (q[0] != '\\' || strchr(specials, q[1])) { end[0] = q[0]; end += 1; } p = q + 1; } AMUDP_free(dup); } else { p = tmp; } strcpy(end, p); AMUDP_free(tmp); AMUDP_assert(strlen(result) <= new_len); return result; }
/* ------------------------------------------------------------------------------------ * spawn jobs on local machine * ------------------------------------------------------------------------------------ */ extern int AMUDP_SPMDLocalSpawn(int nproc, int argc, char **argv) { /* just a simple fork/exec */ int i; if (!AMUDP_SPMDSpawnRunning) { AMUDP_Err("Spawn functions should never be run directly - only passed to AMUDP_SPMDStartup()"); return FALSE; } for (i = 0; i < nproc; i++) { #if PLATFORM_OS_MSWINDOWS && !PLATFORM_OS_CYGWIN if (_spawnv(_P_NOWAIT, argv[0], argv) == -1) AMUDP_FatalErr("failed _spawnv()"); #elif PLATFORM_ARCH_CRAYX1 { char **nargv = (char **)AMUDP_malloc(sizeof(char *)*(argc+2)); nargv[0] = argv[0]; memcpy(nargv+1,argv,argc*sizeof(char *)); nargv[argc+1] = NULL; if (execsp(nargv, environ, NULL) == -1) AMUDP_FatalErr("failed execsp()"); } #else int forkRet = fork(); if (forkRet == -1) { perror("fork"); return FALSE; } else if (forkRet != 0) continue; /* this is the parent, will go back to the top of the loop */ else { /* this is the child - exec the new process */ /* could close some resources here (like AMUDP_SPMDListenSocket) but not strictly necessary */ #if 0 /* put new process in a separate process group */ if (setsid() == -1) perror("setsid"); #endif /* exec the program, with the given arguments */ execv(argv[0], argv); /* if execv returns, an error occurred */ perror("execv"); _exit(1); /* use _exit() to prevent corrupting parent's io buffers */ } /* child */ #endif } return TRUE; }
int AMUDP_SPMDSshSpawn(int nproc, int argc, char **argv, char **extra_env) { int i; const char *ssh_servers; const char *ssh_cmd; const char *ssh_options; const char *ssh_remote_path; char cwd[1024]; const char *p; char *cmd1, *cmd2; size_t cmd1_sz, cmd2_sz; int pid; if (!AMUDP_SPMDSpawnRunning) { AMUDP_Err("Spawn functions should never be run directly - only passed to AMUDP_SPMDStartup()"); return FALSE; } pid = getpid(); ssh_servers = AMUDP_getenv_prefixed_withdefault("SSH_SERVERS",""); if (!strlen(ssh_servers)) { printf("Environment variable SSH_SERVERS is missing.\n"); return FALSE; } if (!getcwd(cwd, 1024)) { printf("Error calling getcwd()\n"); return FALSE; } ssh_remote_path = quote_for_local(AMUDP_getenv_prefixed_withdefault("SSH_REMOTE_PATH", cwd)); ssh_cmd = AMUDP_getenv_prefixed_withdefault("SSH_CMD", "ssh"); int isOpenSSH = 0; /* figure out if we're using OpenSSH */ { char cmdtmp[1024]; sprintf(cmdtmp,"%s -v 2>&1 | grep OpenSSH", ssh_cmd); FILE *pop = popen(cmdtmp,"r"); while (!feof(pop) && !ferror(pop)) { int next = fgetc(pop); if (next != EOF && !isspace(next)) { isOpenSSH = 1; break; } } pclose(pop); } ssh_options = AMUDP_getenv_prefixed_withdefault("SSH_OPTIONS",""); p = ssh_servers; for (i = 0; i < nproc; i++) { /* check we have enough servers */ const char *end; while (*p && strchr(SSH_SERVERS_DELIM_CHARS, *p)) p++; end = p + strcspn(p, SSH_SERVERS_DELIM_CHARS); if (p == end) { printf("Not enough machines in environment variable SSH_SERVERS to satisfy request for (%i).\n" "Only (%i) machines available: %s\n", nproc, i, ssh_servers); return FALSE; } if (*end) p = end+1; else p = end; } const char *envcmd = AMUDP_getenv_prefixed_withdefault("ENV_CMD", "env"); cmd1_sz = 1; /* terminating nul */ if (extra_env && extra_env[0]) { cmd1_sz += 1 + strlen(envcmd); // "env " for (i=0; extra_env[i]; ++i) { cmd1_sz += strlen(extra_env[i]) + 3; // "'%s' " } } char **quoted_args = (char**)AMUDP_malloc(argc * sizeof(char*)); for (i = 0; i < argc; i++) { AMUDP_assert(argv[i]); quoted_args[i] = quote_for_local(argv[i]); cmd1_sz += strlen(quoted_args[i]) + 1; // +1 for trailing space } cmd1 = (char *)AMUDP_malloc(cmd1_sz); { char *tmp = cmd1; if (extra_env && extra_env[0]) { tmp += sprintf(tmp, "%s ", envcmd); for (i=0; extra_env[i]; ++i) { tmp += sprintf(tmp, "'%s' ", extra_env[i]); } } for (i = 0; i < argc; i++) { AMUDP_assert(quoted_args[i]); tmp += sprintf(tmp, "%s ", quoted_args[i]); AMUDP_free(quoted_args[i]); } AMUDP_free(quoted_args); AMUDP_assert(!argv[i]); *tmp = '\0'; AMUDP_assert(strlen(cmd1) == cmd1_sz - 1); } cmd2_sz = cmd1_sz + strlen(ssh_remote_path) + 1024; /* estimated */ cmd2 = (char *)AMUDP_malloc(cmd2_sz); p = ssh_servers; for (i = 0; i < nproc; i++) { char ssh_server[255]; const char *end; while (*p && strchr(SSH_SERVERS_DELIM_CHARS, *p)) p++; end = p + strcspn(p, SSH_SERVERS_DELIM_CHARS); AMUDP_assert(p != end); strncpy(ssh_server, p, (end-p)); ssh_server[end-p] = '\0'; /* build the ssh command */ snprintf(cmd2, cmd2_sz, "%s %s %s %s %s %s \" %s cd %s ; %s\" " " || ( echo \"connection to %s failed.\" ; kill %i ) " "%s", ssh_cmd, (isOpenSSH?"-f":""), /* go into background and nullify stdin */ #if SSH_SUPRESSNEWKEYPROMPT (isOpenSSH?"-o 'StrictHostKeyChecking no'":""), #else "", #endif #if SSH_PREVENTRSHFALLBACK (isOpenSSH?"-o 'FallBackToRsh no'":""), #else "", #endif ssh_options, ssh_server, (AMUDP_SilentMode?"":"echo connected to \\$HOST... ;"), ssh_remote_path, cmd1, ssh_server, pid, #if SSH_PARALLELSPAWN "&" #else "" #endif ); if (!AMUDP_SilentMode) printf("system(%s)\n", cmd2); fflush(stdout); if (system(cmd2) == -1) { printf("Failed to call system() to spawn"); AMUDP_free(cmd1); AMUDP_free(cmd2); return FALSE; } if (*end) p = end+1; else p = end; } AMUDP_free(cmd1); AMUDP_free(cmd2); return TRUE; }
/* ------------------------------------------------------------------------------------ * spawn jobs on local machine * ------------------------------------------------------------------------------------ */ extern int AMUDP_SPMDLocalSpawn(int nproc, int argc, char **argv, char **extra_env) { /* just a simple fork/exec */ int i; if (!AMUDP_SPMDSpawnRunning) { AMUDP_Err("Spawn functions should never be run directly - only passed to AMUDP_SPMDStartup()"); return FALSE; } /* Temporarily assume values from extra_env[] (which we modify in-place) */ char **save_env = NULL; int envc = 0; if (extra_env && extra_env[0]) { for (envc=0; extra_env[envc]; ++envc) {/*empty*/} save_env = (char **)AMUDP_malloc(sizeof(char *)*envc); for (i=0;i<envc;++i) { char *var = extra_env[i]; char *delim = strchr(var,'='); AMUDP_assert(delim); *delim = '\0'; save_env[i] = getenv(var); setenv(var,delim+1,1); } } for (i = 0; i < nproc; i++) { #if PLATFORM_OS_MSWINDOWS && !PLATFORM_OS_CYGWIN if (_spawnv(_P_NOWAIT, argv[0], argv) == -1) AMUDP_FatalErr("failed _spawnv()"); #elif PLATFORM_ARCH_CRAYX1 { char **nargv = (char **)AMUDP_malloc(sizeof(char *)*(argc+2)); nargv[0] = argv[0]; memcpy(nargv+1,argv,argc*sizeof(char *)); nargv[argc+1] = NULL; if (execsp(nargv, environ, NULL) == -1) AMUDP_FatalErr("failed execsp()"); } #else int forkRet = fork(); if (forkRet == -1) { perror("fork"); return FALSE; } else if (forkRet != 0) continue; /* this is the parent, will go back to the top of the loop */ else { /* this is the child - exec the new process */ /* could close some resources here (like AMUDP_SPMDListenSocket) but not strictly necessary */ #if 0 /* put new process in a separate process group */ if (setsid() == -1) perror("setsid"); #endif /* exec the program, with the given arguments */ execv(argv[0], argv); /* if execv returns, an error occurred */ perror("execv"); _exit(1); /* use _exit() to prevent corrupting parent's io buffers */ } /* child */ #endif } /* Restore saved environment var(s) */ for (i=0;i<envc;++i) { char *name = extra_env[i]; if (save_env[i]) { setenv(name,save_env[i],1); } else { unsetenv(name); } /* Revert our in-place modification of extra_env[]: */ name[strlen(name)] = '='; } AMUDP_free(save_env); return TRUE; }