static void handle_childsig(int childpid, int childstatus, int stop) { int __sig; if (stop == TRUE) __sig = WSTOPSIG(childstatus); else __sig = WTERMSIG(childstatus); switch (__sig) { case SIGSTOP: if (stop != TRUE) return; debugf("Sending PTRACE_DETACH (and then KILL)\n"); ptrace(PTRACE_DETACH, childpid, NULL, NULL); kill(childpid, SIGKILL); reap_child(childpid); return; case SIGALRM: debugf("got a alarm signal from pid %d\n", childpid); break; case SIGFPE: case SIGSEGV: case SIGKILL: case SIGPIPE: case SIGABRT: case SIGBUS: if (stop == TRUE) debugf("Child %d was stopped by %s\n", childpid, strsignal(WSTOPSIG(childstatus))); else debugf("got a signal from pid %d (%s)\n", childpid, strsignal(WTERMSIG(childstatus))); reap_child(childpid); return; default: if (__sig >= SIGRTMIN) { debugf("Child %d got RT signal (%d). Ignoring.\n", childpid, __sig); return; } if (stop == TRUE) debugf("Child %d was stopped by unhandled signal (%s).\n", childpid, strsignal(WSTOPSIG(childstatus))); else debugf("** Child got an unhandled signal (%d)\n", WTERMSIG(childstatus)); return; } }
static void create_new_objects(void) { int dbnum; prep_status("Adding support functions to new cluster"); /* * Technically, we only need to install these support functions in new * databases that also exist in the old cluster, but for completeness we * process all new databases. */ for (dbnum = 0; dbnum < new_cluster.dbarr.ndbs; dbnum++) { DbInfo *new_db = &new_cluster.dbarr.dbs[dbnum]; /* skip db we already installed */ if (strcmp(new_db->db_name, "template1") != 0) install_support_functions_in_new_db(new_db->db_name); } check_ok(); prep_status("Restoring database schemas in the new cluster\n"); for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++) { char sql_file_name[MAXPGPATH], log_file_name[MAXPGPATH]; DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum]; pg_log(PG_STATUS, "%s", old_db->db_name); snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid); snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid); /* * pg_dump only produces its output at the end, so there is little * parallelism if using the pipe. */ parallel_exec_prog(log_file_name, NULL, "\"%s/pg_restore\" %s --exit-on-error --verbose --dbname \"%s\" \"%s\"", new_cluster.bindir, cluster_conn_opts(&new_cluster), old_db->db_name, sql_file_name); } /* reap all children */ while (reap_child(true) == true) ; end_progress_output(); check_ok(); /* regenerate now that we have objects in the databases */ get_db_and_rel_infos(&new_cluster); uninstall_support_functions_from_new_cluster(); }
static void create_new_objects(void) { int dbnum; prep_status("Restoring database schemas in the new cluster\n"); for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++) { char sql_file_name[MAXPGPATH], log_file_name[MAXPGPATH]; DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum]; PQExpBufferData connstr, escaped_connstr; initPQExpBuffer(&connstr); appendPQExpBuffer(&connstr, "dbname="); appendConnStrVal(&connstr, old_db->db_name); initPQExpBuffer(&escaped_connstr); appendShellString(&escaped_connstr, connstr.data); termPQExpBuffer(&connstr); pg_log(PG_STATUS, "%s", old_db->db_name); snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid); snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid); /* * pg_dump only produces its output at the end, so there is little * parallelism if using the pipe. */ parallel_exec_prog(log_file_name, NULL, "\"%s/pg_restore\" %s --exit-on-error --verbose --dbname %s \"%s\"", new_cluster.bindir, cluster_conn_opts(&new_cluster), escaped_connstr.data, sql_file_name); termPQExpBuffer(&escaped_connstr); } /* reap all children */ while (reap_child(true) == true) ; end_progress_output(); check_ok(); /* * We don't have minmxids for databases or relations in pre-9.3 clusters, * so set those after we have restored the schema. */ if (GET_MAJOR_VERSION(old_cluster.major_version) < 903) set_frozenxids(true); /* regenerate now that we have objects in the databases */ get_db_and_rel_infos(&new_cluster); }
void mitm_run(void) { u_char buf[8192]; fd_set fds; int i; signal(SIGCHLD, sig_chld); signal(SIGINT, sig_int); if (fcntl(sig_pipe[0], F_SETFL, O_NONBLOCK) == -1 || fcntl(sig_pipe[1], F_SETFL, O_NONBLOCK) == -1) err(1, "fcntl"); if (fcntl(mitm_fd, F_SETFL, O_NONBLOCK) == -1) err(1, "fcntl"); for (;;) { FD_ZERO(&fds); FD_SET(mitm_fd, &fds); FD_SET(sig_pipe[0], &fds); i = MAX(mitm_fd, sig_pipe[0]); if (select(i + 1, &fds, 0, 0, 0) == -1) { if (errno != EINTR) err(1, "select"); } i = sizeof(csin); if (FD_ISSET(sig_pipe[0], &fds)) { while (read(sig_pipe[0], buf, 1) == 1) ; /* empty non-blocking pipe */ reap_child(); } if (FD_ISSET(mitm_fd, &fds)) { client_fd = accept(mitm_fd, (struct sockaddr *)&csin, &i); if (client_fd >= 0) { if (fork() == 0) { close(mitm_fd); mitm_child(); exit(0); } close(client_fd); } else if (errno != EINTR && errno != EWOULDBLOCK) { err(1, "accept"); } } } }
static unsigned int reap_dead_kids(void) { unsigned int i; unsigned int alive = 0; unsigned int reaped = 0; for_each_child(i) { struct childdata *child; pid_t pid; int ret; child = shm->children[i]; pid = child->pid; if (pid == EMPTY_PIDSLOT) continue; if (pid_is_valid(pid) == FALSE) { static bool once = FALSE; if (once != FALSE) return 0; output(0, "Sanity check failed! Found pid %u at pidslot %u!\n", pid, i); dump_childnos(); if (shm->exit_reason == STILL_RUNNING) panic(EXIT_PID_OUT_OF_RANGE); dump_childdata(child); once = TRUE; return 0; } ret = kill(pid, 0); /* If it disappeared, reap it. */ if (ret == -1) { if (errno == ESRCH) { output(0, "pid %u has disappeared. Reaping.\n", pid); reap_child(pid); reaped++; } else { output(0, "problem checking on pid %u (%d:%s)\n", pid, errno, strerror(errno)); } } else { alive++; } if (shm->running_childs == 0) return 0; } if (reaped != 0) output(0, "Reaped %d dead children\n", reaped); return alive; }
void generate_old_dump(void) { int dbnum; mode_t old_umask; prep_status("Creating dump of global objects"); /* run new pg_dumpall binary for globals */ exec_prog(UTILITY_LOG_FILE, NULL, true, "\"%s/pg_dumpall\" %s --globals-only --quote-all-identifiers " "--binary-upgrade %s -f %s", new_cluster.bindir, cluster_conn_opts(&old_cluster), log_opts.verbose ? "--verbose" : "", GLOBALS_DUMP_FILE); check_ok(); prep_status("Creating dump of database schemas\n"); /* * Set umask for this function, all functions it calls, and all * subprocesses/threads it creates. We can't use fopen_priv() as Windows * uses threads and umask is process-global. */ old_umask = umask(S_IRWXG | S_IRWXO); /* create per-db dump files */ for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++) { char sql_file_name[MAXPGPATH], log_file_name[MAXPGPATH]; DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum]; pg_log(PG_STATUS, "%s", old_db->db_name); snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid); snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid); parallel_exec_prog(log_file_name, NULL, "\"%s/pg_dump\" %s --schema-only --quote-all-identifiers " "--binary-upgrade --format=custom %s --file=\"%s\" \"%s\"", new_cluster.bindir, cluster_conn_opts(&old_cluster), log_opts.verbose ? "--verbose" : "", sql_file_name, old_db->db_name); } /* reap all children */ while (reap_child(true) == true) ; umask(old_umask); end_progress_output(); check_ok(); }
static void kill_all_kids(void) { unsigned int i; shm->spawn_no_more = TRUE; reap_dead_kids(); /* Wait for all the children to exit. */ while (shm->running_childs > 0) { int children_seen = 0; /* Ok, some kids are still alive. 'help' them along with a SIGKILL */ for_each_child(i) { pid_t pid; int ret; pid = shm->children[i]->pid; if (pid == EMPTY_PIDSLOT) continue; /* if we find corruption, just skip over it. */ if (pid_is_valid(pid) == FALSE) continue; children_seen++; ret = kill(pid, SIGKILL); /* check we don't have anything stale in the pidlist */ if (ret == -1) { if (errno == ESRCH) reap_child(pid); } } if (children_seen == 0) shm->running_childs = 0; /* Check that no dead children hold locks. */ check_all_locks(); /* wait a second to give kids a chance to exit. */ sleep(1); } /* Just to be sure, clear out the pid slots. */ for_each_child(i) shm->children[i]->pid = EMPTY_PIDSLOT; }
/* * transfer_all_new_tablespaces() * * Responsible for upgrading all database. invokes routines to generate mappings and then * physically link the databases. */ void transfer_all_new_tablespaces(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr, char *old_pgdata, char *new_pgdata) { if (user_opts.transfer_mode == TRANSFER_MODE_LINK) pg_log(PG_REPORT, "Linking user relation files\n"); else pg_log(PG_REPORT, "Copying user relation files\n"); /* * Transferring files by tablespace is tricky because a single database * can use multiple tablespaces. For non-parallel mode, we just pass a * NULL tablespace path, which matches all tablespaces. In parallel mode, * we pass the default tablespace and all user-created tablespaces and let * those operations happen in parallel. */ if (user_opts.jobs <= 1) parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata, new_pgdata, NULL); else { int tblnum; /* transfer default tablespace */ parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata, new_pgdata, old_pgdata); for (tblnum = 0; tblnum < os_info.num_old_tablespaces; tblnum++) parallel_transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata, new_pgdata, os_info.old_tablespaces[tblnum]); /* reap all children */ while (reap_child(true) == true) ; } end_progress_output(); check_ok(); return; }
void generate_old_dump(void) { int dbnum; prep_status("Creating dump of global objects"); /* run new pg_dumpall binary for globals */ exec_prog(UTILITY_LOG_FILE, NULL, true, "\"%s/pg_dumpall\" %s --schema-only --globals-only --binary-upgrade %s -f %s", new_cluster.bindir, cluster_conn_opts(&old_cluster), log_opts.verbose ? "--verbose" : "", GLOBALS_DUMP_FILE); check_ok(); prep_status("Creating dump of database schemas\n"); /* create per-db dump files */ for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++) { char sql_file_name[MAXPGPATH], log_file_name[MAXPGPATH]; DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum]; pg_log(PG_STATUS, "%s", old_db->db_name); snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid); snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid); parallel_exec_prog(log_file_name, NULL, "\"%s/pg_dump\" %s --schema-only --binary-upgrade --format=custom %s --file=\"%s\" \"%s\"", new_cluster.bindir, cluster_conn_opts(&old_cluster), log_opts.verbose ? "--verbose" : "", sql_file_name, old_db->db_name); } /* reap all children */ while (reap_child(true) == true) ; end_progress_output(); check_ok(); }
/* Make sure there's no dead kids lying around. * We need to do this in case the oom killer has been killing them, * otherwise we end up stuck with no child processes. */ static void reap_dead_kids(void) { unsigned int i; unsigned int reaped = 0; for_each_child(i) { struct childdata *child; pid_t pid; int ret; child = shm->children[i]; pid = child->pid; if (pid == EMPTY_PIDSLOT) continue; /* if we find corruption, just skip over it. */ if (pid_is_valid(pid) == FALSE) continue; ret = kill(pid, 0); /* If it disappeared, reap it. */ if (ret == -1) { if (errno == ESRCH) { output(0, "pid %u has disappeared. Reaping.\n", pid); reap_child(pid); reaped++; } else { output(0, "problem checking on pid %u (%d:%s)\n", pid, errno, strerror(errno)); } } if (shm->running_childs == 0) return; } if (reaped != 0) output(0, "Reaped %d dead children\n", reaped); }
static unsigned int reap_dead_kids(void) { unsigned int i; unsigned int alive = 0; unsigned int reaped = 0; for_each_pidslot(i) { pid_t pid; int ret; pid = shm->pids[i]; if (pid == EMPTY_PIDSLOT) continue; ret = kill(pid, 0); /* If it disappeared, reap it. */ if (ret == -1) { if (errno == ESRCH) { output(0, "pid %d has disappeared (oom-killed maybe?). Reaping.\n", pid); reap_child(pid); reaped++; } else { output(0, "problem checking on pid %d (%d:%s)\n", pid, errno, strerror(errno)); } } else { alive++; } if (shm->running_childs == 0) return 0; } if (reaped != 0) output(0, "Reaped %d dead children\n", reaped); return alive; }
static void create_new_objects(void) { int dbnum; prep_status("Restoring database schemas in the new cluster\n"); /* * We cannot process the template1 database concurrently with others, * because when it's transiently dropped, connection attempts would fail. * So handle it in a separate non-parallelized pass. */ for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++) { char sql_file_name[MAXPGPATH], log_file_name[MAXPGPATH]; DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum]; const char *create_opts; /* Process only template1 in this pass */ if (strcmp(old_db->db_name, "template1") != 0) continue; pg_log(PG_STATUS, "%s", old_db->db_name); snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid); snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid); /* * template1 and postgres databases will already exist in the target * installation, so tell pg_restore to drop and recreate them; * otherwise we would fail to propagate their database-level * properties. */ create_opts = "--clean --create"; exec_prog(log_file_name, NULL, true, true, "\"%s/pg_restore\" %s %s --exit-on-error --verbose " "--dbname postgres \"%s\"", new_cluster.bindir, cluster_conn_opts(&new_cluster), create_opts, sql_file_name); break; /* done once we've processed template1 */ } for (dbnum = 0; dbnum < old_cluster.dbarr.ndbs; dbnum++) { char sql_file_name[MAXPGPATH], log_file_name[MAXPGPATH]; DbInfo *old_db = &old_cluster.dbarr.dbs[dbnum]; const char *create_opts; /* Skip template1 in this pass */ if (strcmp(old_db->db_name, "template1") == 0) continue; pg_log(PG_STATUS, "%s", old_db->db_name); snprintf(sql_file_name, sizeof(sql_file_name), DB_DUMP_FILE_MASK, old_db->db_oid); snprintf(log_file_name, sizeof(log_file_name), DB_DUMP_LOG_FILE_MASK, old_db->db_oid); /* * template1 and postgres databases will already exist in the target * installation, so tell pg_restore to drop and recreate them; * otherwise we would fail to propagate their database-level * properties. */ if (strcmp(old_db->db_name, "postgres") == 0) create_opts = "--clean --create"; else create_opts = "--create"; parallel_exec_prog(log_file_name, NULL, "\"%s/pg_restore\" %s %s --exit-on-error --verbose " "--dbname template1 \"%s\"", new_cluster.bindir, cluster_conn_opts(&new_cluster), create_opts, sql_file_name); } /* reap all children */ while (reap_child(true) == true) ; end_progress_output(); check_ok(); /* * We don't have minmxids for databases or relations in pre-9.3 clusters, * so set those after we have restored the schema. */ if (GET_MAJOR_VERSION(old_cluster.major_version) < 903) set_frozenxids(true); /* update new_cluster info now that we have objects in the databases */ get_db_and_rel_infos(&new_cluster); }
/* Generate children*/ static void fork_children(void) { while (shm->running_childs < max_children) { int childno; int pid = 0; if (shm->spawn_no_more == TRUE) return; /* a new child means a new seed, or the new child * will do the same syscalls as the one in the child it's replacing. * (special case startup, or we reseed unnecessarily) */ if (shm->ready == TRUE) reseed(); /* Find a space for it in the pid map */ childno = find_childno(EMPTY_PIDSLOT); if (childno == CHILD_NOT_FOUND) { outputerr("## Pid map was full!\n"); dump_childnos(); exit_main_fail(); } fflush(stdout); pid = fork(); if (pid == 0) { /* Child process. */ struct childdata *child = shm->children[childno]; init_child(child, childno); child_process(); debugf("child %d %d exiting.\n", childno, getpid()); close_logfile(&this_child->logfile); reap_child(child->pid); _exit(EXIT_SUCCESS); } else { if (pid == -1) { /* We failed, wait for a child to exit before retrying. */ if (shm->running_childs > 0) return; output(0, "couldn't create child! (%s)\n", strerror(errno)); panic(EXIT_FORK_FAILURE); exit_main_fail(); } } shm->children[childno]->pid = pid; shm->running_childs++; debugf("Created child %d (pid:%d) [total:%d/%d]\n", childno, pid, shm->running_childs, max_children); if (shm->exit_reason != STILL_RUNNING) return; } shm->ready = TRUE; debugf("created enough children\n"); }
static void handle_child(pid_t childpid, int childstatus) { switch (childpid) { case 0: //debugf("Nothing changed. children:%d\n", shm->running_childs); break; case -1: if (shm->exit_reason != STILL_RUNNING) return; if (errno == ECHILD) { unsigned int i; bool seen = FALSE; debugf("All children exited!\n"); for_each_child(i) { struct childdata *child; child = shm->children[i]; if (child->pid != EMPTY_PIDSLOT) { if (pid_alive(child->pid) == -1) { debugf("Removing %d from pidmap\n", child->pid); child->pid = EMPTY_PIDSLOT; shm->running_childs--; } else { debugf("%d looks still alive! ignoring.\n", child->pid); } seen = TRUE; } } if (seen == FALSE) shm->running_childs = 0; break; } output(0, "error! (%s)\n", strerror(errno)); break; default: debugf("Something happened to pid %d\n", childpid); if (WIFEXITED(childstatus)) { int childno; childno = find_childno(childpid); if (childno != CHILD_NOT_FOUND) { debugf("Child %d exited after %ld operations.\n", childpid, shm->children[childno]->syscall.op_nr); reap_child(childpid); } break; } else if (WIFSIGNALED(childstatus)) { handle_childsig(childpid, childstatus, FALSE); } else if (WIFSTOPPED(childstatus)) { handle_childsig(childpid, childstatus, TRUE); } else if (WIFCONTINUED(childstatus)) { break; } else { output(0, "erk, wtf\n"); } }
static void handle_child(pid_t childpid, int childstatus) { unsigned int i; int slot; switch (childpid) { case 0: //debugf("Nothing changed. children:%d\n", shm->running_childs); break; case -1: if (shm->exit_reason != STILL_RUNNING) return; if (errno == ECHILD) { debugf("All children exited!\n"); for_each_pidslot(i) { if (shm->pids[i] != EMPTY_PIDSLOT) { if (pid_alive(shm->pids[i]) == -1) { debugf("Removing %d from pidmap\n", shm->pids[i]); shm->pids[i] = EMPTY_PIDSLOT; shm->running_childs--; } else { debugf("%d looks still alive! ignoring.\n", shm->pids[i]); } } } break; } output(0, "error! (%s)\n", strerror(errno)); break; default: debugf("Something happened to pid %d\n", childpid); if (WIFEXITED(childstatus)) { slot = find_pid_slot(childpid); if (slot == PIDSLOT_NOT_FOUND) { /* If we reaped it, it wouldn't show up, so check that. */ if (shm->last_reaped != childpid) { outputerr("## Couldn't find pid slot for %d\n", childpid); shm->exit_reason = EXIT_LOST_PID_SLOT; dump_pid_slots(); } } else { debugf("Child %d exited after %ld syscalls.\n", childpid, shm->child_syscall_count[slot]); reap_child(childpid); } break; } else if (WIFSIGNALED(childstatus)) { switch (WTERMSIG(childstatus)) { case SIGALRM: debugf("got a alarm signal from pid %d\n", childpid); break; case SIGFPE: case SIGSEGV: case SIGKILL: case SIGPIPE: case SIGABRT: debugf("got a signal from pid %d (%s)\n", childpid, strsignal(WTERMSIG(childstatus))); reap_child(childpid); break; default: debugf("** Child got an unhandled signal (%d)\n", WTERMSIG(childstatus)); break; } break; } else if (WIFSTOPPED(childstatus)) { switch (WSTOPSIG(childstatus)) { case SIGALRM: debugf("got an alarm signal from pid %d\n", childpid); break; case SIGSTOP: debugf("Sending PTRACE_DETACH (and then KILL)\n"); ptrace(PTRACE_DETACH, childpid, NULL, NULL); kill(childpid, SIGKILL); reap_child(childpid); break; case SIGFPE: case SIGSEGV: case SIGKILL: case SIGPIPE: case SIGABRT: debugf("Child %d was stopped by %s\n", childpid, strsignal(WTERMSIG(childstatus))); reap_child(childpid); break; default: debugf("Child %d was stopped by unhandled signal (%s).\n", childpid, strsignal(WSTOPSIG(childstatus))); break; } break; } else if (WIFCONTINUED(childstatus)) { break; } else { output(0, "erk, wtf\n"); } }
/* * parallel_exec_prog * * This has the same API as exec_prog, except it does parallel execution, * and therefore must throw errors and doesn't return an error status. */ void parallel_exec_prog(const char *log_file, const char *opt_log_file, const char *fmt,...) { va_list args; char cmd[MAX_STRING]; #ifndef WIN32 pid_t child; #else HANDLE child; exec_thread_arg *new_arg; #endif va_start(args, fmt); vsnprintf(cmd, sizeof(cmd), fmt, args); va_end(args); if (user_opts.jobs <= 1) /* throw_error must be true to allow jobs */ exec_prog(log_file, opt_log_file, true, "%s", cmd); else { /* parallel */ #ifdef WIN32 if (thread_handles == NULL) thread_handles = pg_malloc(user_opts.jobs * sizeof(HANDLE)); if (exec_thread_args == NULL) { int i; exec_thread_args = pg_malloc(user_opts.jobs * sizeof(exec_thread_arg *)); /* * For safety and performance, we keep the args allocated during * the entire life of the process, and we don't free the args in a * thread different from the one that allocated it. */ for (i = 0; i < user_opts.jobs; i++) exec_thread_args[i] = pg_malloc0(sizeof(exec_thread_arg)); } cur_thread_args = (void **) exec_thread_args; #endif /* harvest any dead children */ while (reap_child(false) == true) ; /* must we wait for a dead child? */ if (parallel_jobs >= user_opts.jobs) reap_child(true); /* set this before we start the job */ parallel_jobs++; /* Ensure stdio state is quiesced before forking */ fflush(NULL); #ifndef WIN32 child = fork(); if (child == 0) /* use _exit to skip atexit() functions */ _exit(!exec_prog(log_file, opt_log_file, true, "%s", cmd)); else if (child < 0) /* fork failed */ pg_fatal("could not create worker process: %s\n", strerror(errno)); #else /* empty array element are always at the end */ new_arg = exec_thread_args[parallel_jobs - 1]; /* Can only pass one pointer into the function, so use a struct */ if (new_arg->log_file) pg_free(new_arg->log_file); new_arg->log_file = pg_strdup(log_file); if (new_arg->opt_log_file) pg_free(new_arg->opt_log_file); new_arg->opt_log_file = opt_log_file ? pg_strdup(opt_log_file) : NULL; if (new_arg->cmd) pg_free(new_arg->cmd); new_arg->cmd = pg_strdup(cmd); child = (HANDLE) _beginthreadex(NULL, 0, (void *) win32_exec_prog, new_arg, 0, NULL); if (child == 0) pg_fatal("could not create worker thread: %s\n", strerror(errno)); thread_handles[parallel_jobs - 1] = child; #endif } return; }
/* * parallel_transfer_all_new_dbs * * This has the same API as transfer_all_new_dbs, except it does parallel execution * by transferring multiple tablespaces in parallel */ void parallel_transfer_all_new_dbs(DbInfoArr *old_db_arr, DbInfoArr *new_db_arr, char *old_pgdata, char *new_pgdata, char *old_tablespace) { #ifndef WIN32 pid_t child; #else HANDLE child; transfer_thread_arg *new_arg; #endif if (user_opts.jobs <= 1) /* throw_error must be true to allow jobs */ transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata, new_pgdata, NULL); else { /* parallel */ #ifdef WIN32 if (thread_handles == NULL) thread_handles = pg_malloc(user_opts.jobs * sizeof(HANDLE)); if (transfer_thread_args == NULL) { int i; transfer_thread_args = pg_malloc(user_opts.jobs * sizeof(transfer_thread_arg *)); /* * For safety and performance, we keep the args allocated during * the entire life of the process, and we don't free the args in a * thread different from the one that allocated it. */ for (i = 0; i < user_opts.jobs; i++) transfer_thread_args[i] = pg_malloc0(sizeof(transfer_thread_arg)); } cur_thread_args = (void **) transfer_thread_args; #endif /* harvest any dead children */ while (reap_child(false) == true) ; /* must we wait for a dead child? */ if (parallel_jobs >= user_opts.jobs) reap_child(true); /* set this before we start the job */ parallel_jobs++; /* Ensure stdio state is quiesced before forking */ fflush(NULL); #ifndef WIN32 child = fork(); if (child == 0) { transfer_all_new_dbs(old_db_arr, new_db_arr, old_pgdata, new_pgdata, old_tablespace); /* if we take another exit path, it will be non-zero */ /* use _exit to skip atexit() functions */ _exit(0); } else if (child < 0) /* fork failed */ pg_fatal("could not create worker process: %s\n", strerror(errno)); #else /* empty array element are always at the end */ new_arg = transfer_thread_args[parallel_jobs - 1]; /* Can only pass one pointer into the function, so use a struct */ new_arg->old_db_arr = old_db_arr; new_arg->new_db_arr = new_db_arr; if (new_arg->old_pgdata) pg_free(new_arg->old_pgdata); new_arg->old_pgdata = pg_strdup(old_pgdata); if (new_arg->new_pgdata) pg_free(new_arg->new_pgdata); new_arg->new_pgdata = pg_strdup(new_pgdata); if (new_arg->old_tablespace) pg_free(new_arg->old_tablespace); new_arg->old_tablespace = old_tablespace ? pg_strdup(old_tablespace) : NULL; child = (HANDLE) _beginthreadex(NULL, 0, (void *) win32_transfer_all_new_dbs, new_arg, 0, NULL); if (child == 0) pg_fatal("could not create worker thread: %s\n", strerror(errno)); thread_handles[parallel_jobs - 1] = child; #endif } return; }
static void handle_children() { int childpid, childstatus; unsigned int i; childpid = waitpid(-1, &childstatus, WUNTRACED | WCONTINUED); switch (childpid) { case 0: debugf("[%d] Nothing changed. children:%d\n", getpid(), shm->running_childs); break; case -1: if (errno == ECHILD) { debugf("[%d] All children exited!\n", getpid()); for (i = 0; i < shm->nr_childs; i++) { if (shm->pids[i] != -1) { debugf("[%d] Removing %d from pidmap\n", getpid(), shm->pids[i]); shm->pids[i] = -1; shm->running_childs--; } } break; } output("error! (%s)\n", strerror(errno)); break; default: debugf("[%d] Something happened to pid %d\n", getpid(), childpid); if (WIFEXITED(childstatus)) { debugf("[%d] Child %d exited\n", getpid(), childpid); reap_child(childpid); break; } else if (WIFSIGNALED(childstatus)) { switch (WTERMSIG(childstatus)) { case SIGFPE: case SIGSEGV: case SIGKILL: case SIGALRM: case SIGPIPE: case SIGABRT: debugf("[%d] got a signal (%s)\n", getpid(), strsignal(WTERMSIG(childstatus))); reap_child(childpid); break; default: debugf("[%d] ** Child got an unhandled signal (%d)\n", getpid(), WTERMSIG(childstatus)); break; } break; } else if (WIFSTOPPED(childstatus)) { debugf("[%d] Child was stopped by %d.", getpid(), WSTOPSIG(childstatus)); kill(childpid, SIGKILL); reap_child(childpid); } else if (WIFCONTINUED(childstatus)) { break; } else { output("erk, wtf\n"); } } }