int cook_script(string_list_ty *wlp) { int retval; graph_ty *gp; graph_build_status_ty gb_status; graph_walk_status_ty gw_status; /* * set interrupts to catch * * Note that tee(1) [see listing.c] must ignore them * for the generated messages to appear in the log file. */ trace(("cook(wlp = %08lX)\n{\n", wlp)); desist_enable(); /* * Build the dependency graph. */ retval = 0; gp = graph_new(); gb_status = graph_build_list(gp, wlp, graph_build_preference_error, 0); if (option_test(OPTION_REASON)) graph_print_statistics(gp); switch (gb_status) { case graph_build_status_error: retval = 1; break; case graph_build_status_backtrack: /* assert(0); */ retval = 1; break; case graph_build_status_success: break; } /* * Walk the dependency graph. */ if (retval == 0) { gw_status = graph_walk_script(gp); switch (gw_status) { case graph_walk_status_uptodate: case graph_walk_status_uptodate_done: case graph_walk_status_done: break; case graph_walk_status_done_stop: case graph_walk_status_wait: assert(0); /* fall through... */ case graph_walk_status_error: retval = 1; break; } } /* * Release any resources held by the graph. */ graph_delete(gp); /* * Return the result to the caller. */ trace(("return %d;\n", retval)); trace(("}\n")); return retval; }
int cook_auto_required(void) { int retval; graph_ty *gp; graph_build_status_ty gb_status; graph_walk_status_ty gw_status; size_t j; /* * This may have been explicitly forbidden on the command line. */ if (!option_test(OPTION_INCLUDE_COOKED)) return 0; retval = 0; option_set(OPTION_ACTION, OPTION_LEVEL_AUTO, 1); option_set(OPTION_TOUCH, OPTION_LEVEL_AUTO, 0); option_set(OPTION_REASON, OPTION_LEVEL_COOKBOOK, 0); /* * Build the dependency graph. */ gp = graph_new(); if ( !option_test(OPTION_SILENT) && option_test(OPTION_INCLUDE_COOKED_WARNING) ) gp->file_pair = graph_file_pair_new(&cook_auto_list); gb_status = graph_build_list ( gp, &cook_auto_list, graph_build_preference_error, 0 /* not primary, no up-to-date commentary */ ); if (option_test(OPTION_REASON)) graph_print_statistics(gp); switch (gb_status) { case graph_build_status_error: retval = -1; break; case graph_build_status_backtrack: /* assert(0); */ retval = -1; break; case graph_build_status_success: break; } /* * Build a list of non-leaf cook-auto files. */ string_list_destructor(&cook_auto_list_nonleaf); for (j = 0; j < cook_auto_list.nstrings; ++j) { string_ty *fn; fn = cook_auto_list.string[j]; if (!graph_file_leaf_p(gp, fn)) string_list_append(&cook_auto_list_nonleaf, fn); } /* * Walk the dependency graph. */ if (retval == 0) { gw_status = graph_walk(gp); switch (gw_status) { case graph_walk_status_uptodate: case graph_walk_status_uptodate_done: break; case graph_walk_status_done: retval = 1; break; case graph_walk_status_done_stop: case graph_walk_status_wait: assert(0); /* fall through... */ case graph_walk_status_error: retval = -1; break; } } /* * Release any resources held by the graph. */ graph_delete(gp); option_undo(OPTION_REASON, OPTION_LEVEL_COOKBOOK); option_undo(OPTION_ACTION, OPTION_LEVEL_AUTO); option_undo(OPTION_TOUCH, OPTION_LEVEL_AUTO); return retval; }
time_t cook_mtime_newest(const opcode_context_ty *ocp, string_ty *path, long *depth_p, long max_fp_depth) { time_t result; trace(("cook_mtime_newest(path = \"%s\")\n{\n", path->str_text)); if (path->str_text[0] == '/') { result = os_mtime_newest(path); *depth_p = 0; } else { fp_value_ty *prv_fp; string_list_ty sl; size_t j; prv_fp = 0; result = 0; cook_search_list(ocp, &sl); *depth_p = sl.nstrings; for (j = 0; j < sl.nstrings; ++j) { fp_value_ty *fp; string_ty *s1; string_ty *s2; time_t t; s1 = sl.string[j]; s2 = os_path_cat(s1, path); /* * This allows the safe use of fp_search below, * since os_mtime_newest updates the fingerprint * cache for the file. */ t = os_mtime_newest(s2); if (!t) { /* File was not found */ str_free(s2); continue; } trace(("mtime(\"%s\") was %ld\n", s2->str_text, (long)t)); /* File found */ if (!prv_fp) { /* Shallowest file found */ result = t; *depth_p = j; trace(("is the first\n")); if (option_test(OPTION_FINGERPRINT)) { prv_fp = fp_search(s2); if (prv_fp) { /* Look for deeper copies */ str_free(s2); continue; } } str_free(s2); break; } /* Found a deeper version */ assert(option_test(OPTION_FINGERPRINT)); if ((long)j >= max_fp_depth) { str_free(s2); break; } fp = fp_search(s2); str_free(s2); if ( fp && str_equal ( fp->contents_fingerprint, prv_fp->contents_fingerprint ) ) { /* Deeper version is same as shallow one */ if (t > result) result = t; /* do not alter depth */ } else { trace(("was different\n")); break; } } string_list_destructor(&sl); } trace(("return %ld (%d);\n", (long)result, *depth_p)); trace(("}\n")); return result; }
int cook(string_list_ty *wlp) { int retval; graph_ty *gp; graph_build_status_ty gb_status; graph_walk_status_ty gw_status; /* * set interrupts to catch * * Note that tee(1) [see listing.c] must ignore them * for the generated messages to appear in the log file. */ trace(("cook(wlp = %08lX)\n{\n", wlp)); desist_enable(); /* * Build the dependency graph. */ retval = 0; gp = graph_new(); if ( cook_auto_list_nonleaf.nstrings > 0 && cascade_used() && option_test(OPTION_CASCADE) && !option_test(OPTION_SILENT) && option_test(OPTION_INCLUDE_COOKED_WARNING) ) { gp->file_pair = graph_file_pair_new((string_list_ty *) 0); graph_file_pair_foreign_derived(gp->file_pair, &cook_auto_list_nonleaf); } gb_status = graph_build_list(gp, wlp, graph_build_preference_error, 1); if (option_test(OPTION_REASON)) graph_print_statistics(gp); switch (gb_status) { case graph_build_status_error: retval = 1; break; case graph_build_status_backtrack: /* assert(0); */ retval = 1; break; case graph_build_status_success: break; } /* * Walk the dependency graph. */ if (retval == 0) { gw_status = graph_walk(gp); switch (gw_status) { case graph_walk_status_uptodate: case graph_walk_status_uptodate_done: case graph_walk_status_done: break; case graph_walk_status_done_stop: case graph_walk_status_wait: assert(0); /* fall through... */ case graph_walk_status_error: retval = 1; break; } } /* * Release any resources held by the graph. */ graph_delete(gp); /* * Return the result to the caller. */ trace(("return %d;\n", retval)); trace(("}\n")); return retval; }
static int interpret(string_list_ty *result, const string_list_ty *args, const struct expr_position_ty *pp, const struct opcode_context_ty *ocp) { size_t j; int retval; graph_ty *gp; (void)pp; (void)ocp; if (args->nstrings <= 1) return 0; /* * set interrupts to catch * * Note that tee(1) [see listing.c] must ignore them * for the generated messages to appear in the log file. */ trace(("cando\n")); assert(result); assert(args); assert(args->nstrings); retval = 0; desist_enable(); /* * build the graph */ gp = graph_new(); for (j = 1; j < args->nstrings; ++j) { graph_build_status_ty gb_status; /* * Build the dependency graph. */ gb_status = graph_build ( gp, args->string[j], graph_build_preference_backtrack, 0 ); /* * it is only relevant that we know how to build this * graph, not that we walk it. */ switch (gb_status) { case graph_build_status_error: retval = -1; break; case graph_build_status_backtrack: break; case graph_build_status_success: string_list_append(result, args->string[j]); break; } } /* * Release resources held by the graph. */ if (option_test(OPTION_REASON)) graph_print_statistics(gp); graph_delete(gp); return retval; }
int main(int argc, char **argv) { int retval; /* * Some versions of cron(8) and at(1) set SIGCHLD to SIG_IGN. * This is kinda dumb, because it breaks assumprions made in * libc (like pclose, for instance). It also blows away most * of Cook's process handling. We explicitly set the SIGCHLD * signal handling to SIG_DFL to make sure this signal does what * we expect no matter how we are invoked. */ #ifdef SIGCHLD signal(SIGCHLD, SIG_DFL); #else signal(SIGCLD, SIG_DFL); #endif /* * initialize things * (order is critical here) */ progname_set(argv[0]); str_initialize(); id_initialize(); lex_initialize(); /* * parse the COOK environment variable */ arglex_init_from_env(argv[0], argtab); argparse(OPTION_LEVEL_ENVIRONMENT); /* * parse the command line */ arglex_init(argc, argv, argtab); argparse(OPTION_LEVEL_COMMAND_LINE); option_tidy_up(); log_open(); /* * turn on progress stars if they asked for them */ if (option_test(OPTION_STAR)) star_enable(); /* * If we were asked to update the fingerprints, do it here. * We don't actually ant to read in the cookbook. */ if (option.fingerprint_update) { fp_tweak(); quit(0); } /* * read in the cook book * * If there are #include-cooked directives, * we may need to do it more than once. */ if (!option.o_book) fatal_intl(0, i18n("no book found")); for (;;) { int status; size_t j; builtin_initialize(); /* * instanciate the command line variable assignments */ for (j = 0; j < option.o_vardef.nstrings; ++j) { char *s; char *cp; string_ty *name; string_ty *value; string_list_ty wl; opcode_context_ty *ocp; s = option.o_vardef.string[j]->str_text; cp = strchr(s, '='); assert(cp); if (!cp) continue; name = str_n_from_c(s, cp - s); value = str_from_c(cp + 1); str2wl(&wl, value, (char *)0, 0); str_free(value); ocp = opcode_context_new(0, 0); opcode_context_id_assign(ocp, name, id_variable_new(&wl), 0); opcode_context_delete(ocp); str_free(name); string_list_destructor(&wl); } set_command_line_goals(); parse(option.o_book); status = cook_auto_required(); if (status < 0) quit(1); if (!status) break; id_reset(); cook_reset(); } /* * work out what to cook. * If no targets have been given, use the first explicit recipe. */ set_command_line_goals(); if (!option.o_target.nstrings) cook_find_default(&option.o_target); assert(option.o_target.nstrings); /* * cook the target */ if (option.pairs) retval = cook_pairs(&option.o_target); else if (option.script) retval = cook_script(&option.o_target); else if (option.web) retval = cook_web(&option.o_target); else retval = cook(&option.o_target); #ifdef DEBUG fflush_slowly_report(); #endif quit(retval); /*NOTREACHED*/ return 0; }
int main(int argc, char *argv[]) { qRegisterMetaType<CurrentStatusUpdate>("CurrentStatusUpdate"); qRegisterMetaType<Items>("Items"); qRegisterMetaType<std::vector<std::string>>("std::vector<std::string>"); qRegisterMetaType<std::vector<ItemLocation>>("std::vector<ItemLocation>"); qRegisterMetaType<QsLogging::Level>("QsLogging::Level"); qRegisterMetaType<TabSelection::Type>("TabSelection::Type"); QLocale::setDefault(QLocale::C); std::setlocale(LC_ALL, "C"); #if defined(CRASHRPT) && !defined(_DEBUG) CR_INSTALL_INFOW info; memset(&info, 0, sizeof(info)); info.cb = sizeof(info); info.pszAppName = L"Acquisition"; WCHAR wversion[64]; MultiByteToWideChar(CP_UTF8, 0, VERSION_NAME, -1, wversion, sizeof(wversion) / sizeof(wversion[0])); info.pszAppVersion = wversion; // URL for sending reports over HTTP. info.pszUrl = L"https://xyz.is/crashfix/index.php/crashReport/uploadExternal"; // Define delivery transport priorities. info.uPriorities[CR_HTTP] = 1; // Use HTTP. info.uPriorities[CR_SMTP] = CR_NEGATIVE_PRIORITY; // Not use SMTP. info.uPriorities[CR_SMAPI] = CR_NEGATIVE_PRIORITY; // Not use Simple MAPI. // Define flags. info.dwFlags = 0; info.dwFlags |= CR_INST_ALL_POSSIBLE_HANDLERS; // Install all available exception handlers. info.dwFlags |= CR_INST_HTTP_BINARY_ENCODING; // Use binary encoding for HTTP uploads (recommended). CrAutoInstallHelper cr_install_helper(&info); #endif InitModlist(); QApplication a(argc, argv); Filesystem::Init(); QFontDatabase::addApplicationFont(":/fonts/Fontin-SmallCaps.ttf"); QCommandLineParser parser; QCommandLineOption option_test("test"), option_data_dir("data-dir", "Where to save Acquisition data.", "data-dir"); parser.addOption(option_test); parser.addOption(option_data_dir); parser.process(a); if (parser.isSet(option_test)) return test_main(); if (parser.isSet(option_data_dir)) Filesystem::SetUserDir(parser.value(option_data_dir).toStdString()); QsLogging::Logger& logger = QsLogging::Logger::instance(); logger.setLoggingLevel(QsLogging::InfoLevel); const QString sLogPath(QDir(Filesystem::UserDir().c_str()).filePath("log.txt")); QsLogging::DestinationPtr fileDestination( QsLogging::DestinationFactory::MakeFileDestination(sLogPath, true, 10 * 1024 * 1024, 0) ); QsLogging::DestinationPtr debugDestination( QsLogging::DestinationFactory::MakeDebugOutputDestination() ); logger.addDestination(debugDestination); logger.addDestination(fileDestination); QLOG_DEBUG() << "-------------------------------------------------------------------------------"; QLOG_DEBUG() << "Built with Qt" << QT_VERSION_STR << "running on" << qVersion(); LoginDialog login(std::make_unique<Application>()); login.show(); return a.exec(); }
static int interpret(string_list_ty *result, const string_list_ty *args, const struct expr_position_ty *pp, const struct opcode_context_ty *ocp) { size_t j; int retval; graph_ty *gp; graph_build_status_ty gb_status; graph_walk_status_ty gw_status; trace(("cook\n")); (void)pp; (void)ocp; assert(result); assert(args); assert(args->nstrings); if (args->nstrings <= 1) return 0; /* * set interrupts to catch * * Note that tee(1) [see listing.c] must ignore them * for the generated messages to appear in the log file. */ desist_enable(); /* * Build the dependency graph. */ retval = 0; gp = graph_new(); for (j = 1; j < args->nstrings; ++j) { gb_status = graph_build ( gp, args->string[j], graph_build_preference_backtrack, 0 ); switch (gb_status) { case graph_build_status_error: retval = -1; break; case graph_build_status_backtrack: break; case graph_build_status_success: string_list_append(result, args->string[j]); break; } } /* * Walk the dependency graph. */ if (retval >= 0) { if (option_test(OPTION_REASON)) graph_print_statistics(gp); gw_status = graph_walk(gp); switch (gw_status) { case graph_walk_status_uptodate: case graph_walk_status_uptodate_done: case graph_walk_status_done: break; case graph_walk_status_done_stop: case graph_walk_status_wait: assert(0); /* fall through... */ case graph_walk_status_error: retval = -1; break; } } /* * Release resources held by the graph. */ graph_delete(gp); return retval; }
static opcode_status_ty script(const opcode_ty *op, opcode_context_ty *ocp) { const opcode_command_ty *this; opcode_status_ty status; string_list_ty *wlp; string_ty *isp; string_list_ty *flags_words; flag_ty *flags; /* * pop the arguments off the value stack */ trace(("opcode_command::script()\n{\n")); this = (const opcode_command_ty *)op; status = opcode_status_success; if (this->input) { string_list_ty *islp; islp = opcode_context_string_list_pop(ocp); isp = wl2str(islp, 0, islp->nstrings - 1, (char *)0); string_list_delete(islp); } else isp = 0; flags_words = opcode_context_string_list_pop(ocp); wlp = opcode_context_string_list_pop(ocp); /* * set the flags */ flags = flag_recognize(flags_words, &this->pos); string_list_delete(flags_words); if (!flags) { status = opcode_status_error; goto done; } flag_set_options(flags, OPTION_LEVEL_EXECUTE); flag_delete(flags); /* * If the command is empty (e.g. [print ...]; or [write ...];) * do nothing at all. */ if (wlp->nstrings == 0) goto done; /* * echo the command if required */ if (!option_test(OPTION_SILENT)) { string_ty *s1; string_ty *s2; s1 = wl2str(wlp, 0, wlp->nstrings, " "); s2 = str_quote_shell(s1); str_free(s1); if (option_test(OPTION_TELL_POSITION)) { string_ty *s3; s3 = str_quote_shell(this->pos.pos_name); printf ( "echo %s: %d: %s\n", s3->str_text, this->pos.pos_line, s2->str_text ); str_free(s3); } else { printf("echo %s\n", s2->str_text); } str_free(s2); } /* * script the command if required */ if (option_test(OPTION_ACTION)) { size_t j; printf("("); for (j = 0; j < wlp->nstrings; ++j) printf(" %s", wlp->string[j]->str_text); printf(" )"); if (isp) { char fubar[50]; time_t t; int nl; time(&t); if (t < 0) t = 0; snprintf ( fubar, sizeof(fubar), "fubar%ldfubar%ldfubar", (long)t, (long)t ); nl = ( isp->str_length && isp->str_text[isp->str_length - 1] != '\n' ); printf ( " << '%s'\n%s%s%s", fubar, isp->str_text, (nl ? "\n" : ""), fubar ); } printf("\n"); if (!option_test(OPTION_ERROK)) printf("test $? -eq 0 || exit 1\n"); } /* * rescind the flag settings */ option_undo_level(OPTION_LEVEL_EXECUTE); /* * release the arguments and exit */ done: string_list_delete(wlp); if (isp) str_free(isp); trace(("return %s;\n", opcode_status_name(status))); trace(("}\n")); return status; }
static opcode_status_ty execute(const opcode_ty *op, opcode_context_ty *ocp) { const opcode_command_ty *this; opcode_status_ty status; string_list_ty *wlp; string_list_ty *flags_words; string_ty *isp; char *cmd; flag_ty *flags; /* * pop the arguments off the value stack */ trace(("opcode_command::execute()\n{\n")); this = (const opcode_command_ty *)op; status = opcode_status_success; isp = 0; flags_words = 0; flags = 0; wlp = 0; if (ocp->pid) { ocp->pid = 0; wlp = ocp->wlp; ocp->wlp = 0; goto resume; } if (this->input) { string_list_ty *islp; islp = opcode_context_string_list_pop(ocp); isp = wl2str(islp, 0, islp->nstrings - 1, (char *)0); string_list_delete(islp); } flags_words = opcode_context_string_list_pop(ocp); wlp = opcode_context_string_list_pop(ocp); /* * take care of the options */ flags = flag_recognize(flags_words, &this->pos); if (!flags) { /* * Error message already printed. */ status = opcode_status_error; goto done; } string_list_delete(flags_words); flags_words = 0; flag_set_options(flags, OPTION_LEVEL_EXECUTE); flag_delete(flags); flags = 0; /* * echo the command if required */ if (!option_test(OPTION_SILENT)) { string_ty *cp; /* * If the command has not been silenced, * form it into a string and echo it. */ cp = wl2str(wlp, 0, wlp->nstrings - 1, (char *)0); if (option_test(OPTION_TELL_POSITION)) { error_raw ( "%s: %d: %s", this->pos.pos_name->str_text, this->pos.pos_line, cp->str_text ); } else { error_raw("%s", cp->str_text); } str_free(cp); } /* * execute the command if required */ if (option_test(OPTION_ACTION)) { /* * emit suitable progress stars */ if (option_test(OPTION_SILENT)) { if (echo_command(wlp)) star_eoln(); else star_bang(); } else star_sync(); /* * invalidate the stat cache */ if (option_test(OPTION_INVALIDATE_STAT_CACHE)) { size_t j; for (j = 0; j < wlp->nstrings; ++j) if (os_clear_stat(wlp->string[j])) status = opcode_status_error; } /* * prepare for metering */ #ifdef HAVE_WAIT3 if (option_test(OPTION_METER)) { if (!ocp->meter_p) ocp->meter_p = meter_alloc(); meter_begin(ocp->meter_p); } #endif /* * run the command */ status = spawn(wlp, isp, &ocp->pid, ocp->host_binding, ocp); if (status == opcode_status_wait) { trace(("...wait\n")); ocp->wlp = wlp; wlp = 0; goto done; } if (status == opcode_status_error) goto done; resume: /* * Finish metering. */ #ifdef HAVE_WAIT3 if (option_test(OPTION_METER)) { assert(ocp->meter_p); meter_print(ocp->meter_p); } #endif /* * Echo the exit status of the command, if it was not * successful. The error flag will also be examined (it * changes the text of the message, too) to see if we * should terminate successfully or with and error. */ cmd = (wlp->nstrings ? wlp->string[0]->str_text : ""); if ( exit_status ( cmd, ocp->exit_status, option_test(OPTION_ERROK) ) ) { status = opcode_status_error; } } /* * rescind the flag settings */ option_undo_level(OPTION_LEVEL_EXECUTE); /* * release the arguments and exit */ done: if (flags_words) string_list_delete(flags_words); if (flags) flag_delete(flags); if (wlp) string_list_delete(wlp); if (isp) str_free(isp); trace(("return %s;\n", opcode_status_name(status))); trace(("}\n")); return status; }
static opcode_status_ty spawn(string_list_ty *args, string_ty *input, int *pid_p, string_ty *host_binding, opcode_context_ty *ocp) { size_t j; int fd; string_ty *iname; int pid; opcode_status_ty status; static char **argv; static size_t argvlen; string_list_ty cmd; sub_context_ty *scp; char *shell; trace(("spawn()\n{\n")); assert(args); /* * build the input file, if required */ status = opcode_status_error; fd = -1; iname = 0; if (input) { /* * He has given a string to be used as input to the command, * so write it out to a file, and then redirect the input. */ iname = temporary_filename(); fd = open(iname->str_text, O_RDWR | O_CREAT | O_TRUNC, 0666); if (fd < 0) fatal_intl_open(iname->str_text); if (unlink(iname->str_text)) { error_intl_unlink(iname->str_text); goto done; } if ( write(fd, input->str_text, input->str_length) < 0 || lseek(fd, 0L, SEEK_SET) ) { error_intl_write(iname->str_text); goto done; } } /* * See if there is a host_binding in effect. */ if (host_binding) { static string_ty *key; string_list_ty *slp; string_ty *s; string_ty *rcmd; string_ty *rcmdq; string_ty *result_fn; FILE *result_fp; string_ty *cwd; string_ty *script_fn; FILE *script_fp; /* * Work out the name of the remote shell command. */ if (!key) key = str_from_c("parallel_rsh"); slp = id_var_search(ocp, key); if (slp) string_list_copy_constructor(&cmd, slp); else { static string_ty *rsh; if (!rsh) rsh = str_from_c(CONF_REMOTE_SHELL); string_list_constructor(&cmd); string_list_append(&cmd, rsh); } /* * Add the name of the host. */ string_list_append(&cmd, host_binding); /* * The remote end will need to change directory to where * we are now, since it is *highly* unlikely that we are * in our home directory. */ cwd = os_curdir(); /* do not str_free this */ /* * We are going to create a small shell script contining * the command to be executed. This deals with various * weird and obscure $SHELL settings, and it also deals * with command lines so long that rsh barfs. * * Yes, there is a potential race condition here. * Between writing the script and executing it, we * could be tricked into executing something else. * Especially if there is a symlink lying in wait. */ script_fn = dot_temporary_filename(); script_fp = fopen_and_check(script_fn->str_text, "w"); /* * what shell are we using */ shell = getenv("SHELL"); if (!shell || !*shell) shell = CONF_SHELL; fprintf(script_fp, "#!%s\n", shell); /* * Now write the actual command to the script file. */ rcmd = wl2str(args, 0, args->nstrings, (char *)0); fprintf(script_fp, "%s\n", rcmd->str_text); str_free(rcmd); fflush_and_check(script_fp, script_fn->str_text); fclose_and_check(script_fp, script_fn->str_text); chmod_and_check(script_fn->str_text, 0755); /* * This is the result file. It is where the remote * command will stash its exit status. We seed it with * failure in case the rsh fails altogether. * * And, yes, there is a race conditon and we could be * lied to if they are quick. */ result_fn = dot_temporary_filename(); result_fp = fopen_and_check(result_fn->str_text, "w"); fprintf(result_fp, "42\n"); fflush_and_check(result_fp, result_fn->str_text); fclose_and_check(result_fp, result_fn->str_text); /* * Because rsh(1) always returns an exit status of zero, * we have to make other arrangements to obtain the exit * status. * * We actually need to be a more devious, so that we can * get the exit status back. Write it to a temporary * file server side, then read and remove it client side. * * This is the server side command. It gets quoted * again to protect it from the client side shell. * * We use sh explicitly, because the shell at the other * end could be csh, tcsh, or something even stranger, * and we need to guarantee the command will execute * exactly the way we need. */ rcmd = str_format ( "sh -c 'cd %s && sh %s %s/%s; echo $? > %s/%s'", cwd->str_text, (option_test(OPTION_ERROK) ? "-c" : "-ce"), cwd->str_text, script_fn->str_text, cwd->str_text, result_fn->str_text ); rcmdq = str_quote_shell(rcmd); str_free(rcmd); string_list_append(&cmd, rcmdq); str_free(rcmdq); /* * This is the rest of the server side command. * * On some systems this gives ``Text file busy'' errors, * as if the script file has yet to be released by * the kernel. Since we *aren't* executing it when the * RM command runs, I have no idea how to avoid this * (other than ignoring it with -f). Maybe this is some * kind of NFS latency? */ s = str_format ( "&& exit `cat %s;rm -f %s %s`", result_fn->str_text, result_fn->str_text, script_fn->str_text ); string_list_append(&cmd, s); str_free(s); str_free(result_fn); str_free(script_fn); /* * Because the command has a semicolon and back quotes, * we need to hand it to a shell (as alluded to above). * Assemble into a single string. */ rcmd = wl2str(&cmd, 0, cmd.nstrings, (char *)0); string_list_destructor(&cmd); /* * Build the final command to be executed. * It even cleans up after itself. */ s = str_from_c("sh"); string_list_append(&cmd, s); str_free(s); s = str_from_c("-c"); string_list_append(&cmd, s); str_free(s); string_list_append(&cmd, rcmd); str_free(rcmd); } else { /* * build the command */ if (os_execute_magic_characters_list(args)) { string_ty *str; /* * what shell are we using */ shell = getenv("SHELL"); if (!shell || !*shell) shell = CONF_SHELL; string_list_constructor(&cmd); str = str_from_c(shell); string_list_append(&cmd, str); str_free(str); if (option_test(OPTION_ERROK)) str = str_from_c("-c"); else str = str_from_c("-ce"); string_list_append(&cmd, str); str_free(str); str = wl2str(args, 0, args->nstrings - 1, (char *)0); string_list_append(&cmd, str); str_free(str); } else string_list_copy_constructor(&cmd, args); } /* * build the argv array */ if (!argv) { argvlen = cmd.nstrings + 1; argv = mem_alloc(argvlen * sizeof(char *)); } else if (argvlen < cmd.nstrings + 1) { argvlen = cmd.nstrings + 1; argv = mem_change_size(argv, argvlen * sizeof(char *)); } if (cmd.nstrings == 0) { string_list_destructor(&cmd); status = opcode_status_success; goto done; } for (j = 0; j < cmd.nstrings; ++j) argv[j] = cmd.string[j]->str_text; argv[cmd.nstrings] = 0; /* * spawn the child process */ switch (pid = fork()) { case -1: scp = sub_context_new(); sub_errno_set(scp); error_intl(scp, i18n("fork(): $errno")); sub_context_delete(scp); break; case 0: /* * child */ if (fd >= 0) { if (close(0) && errno != EBADF) { string_ty *fn0; int err; err = errno; scp = sub_context_new(); fn0 = subst_intl(scp, "standard input"); /* re-use substitution context */ sub_errno_setx(scp, err); sub_var_set_string(scp, "File_Name", fn0); fatal_intl ( scp, i18n("close $filename: $errno") ); /* NOTREACHED */ sub_context_delete(scp); str_free(fn0); } if (dup(fd) < 0) { scp = sub_context_new(); sub_errno_set(scp); fatal_intl(scp, i18n("dup(): $errno")); /* NOTREACHED */ sub_context_delete(scp); } close(fd); } if (argv[0][0] == '/') execv(argv[0], argv); else execvp(argv[0], argv); scp = sub_context_new(); sub_errno_set(scp); sub_var_set_charstar(scp, "File_Name", argv[0]); fatal_intl(scp, i18n("exec $filename: $errno")); /* NOTREACHED */ sub_context_delete(scp); default: /* * parent */ if (fd >= 0) { close(fd); fd = -1; } string_list_destructor(&cmd); status = opcode_status_wait; trace(("pid = %d;\n", pid)); *pid_p = pid; break; } done: if (fd >= 0) close(fd); if (iname) str_free(iname); trace(("return %s;\n", opcode_status_name(status))); trace(("}\n")); return status; }
static graph_walk_status_ty graph_walk_inner(graph_ty *gp, graph_walk_status_ty (*func)(graph_recipe_ty *, graph_ty *), int nproc) { graph_recipe_list_nrc_ty walk; graph_walk_status_ty status; graph_walk_status_ty status2; graph_recipe_ty *grp; size_t j; size_t walk_pos; itab_ty *itp; string_list_ty single_thread; trace(("graph_walk(gp = %8.8lX, nproc = %d)\n{\n", (long)gp, nproc)); status = graph_walk_status_uptodate; itp = itab_alloc(nproc); /* * Reset the activity counter for each recipe. * * Recipes with no inputs are added to the list at this time, * all of their inputs are satisfied. */ graph_recipe_list_nrc_constructor(&walk); for (j = 0; j < gp->already_recipe->nrecipes; ++j) { grp = gp->already_recipe->recipe[j]; grp->input_satisfied = 0; grp->input_uptodate = 0; if (grp->output->nfiles != 0 && grp->input->nfiles == 0) { grp->input_uptodate = 1; graph_recipe_list_nrc_append(&walk, grp); } } /* * Turn the list upside down. We want to find all of the files * with outputs but no inputs. This is the initial list of file * nodes to walk. */ symtab_walk(gp->already, is_it_a_leaf, &walk); /* * Keep chewing up graph recipe nodes until no more are left to * be processed. */ string_list_constructor(&single_thread); walk_pos = 0; while (walk_pos < walk.nrecipes || itp->load > 0) { /* * Terminate elegantly, if asked to. */ if (desist_requested()) { desist: status = graph_walk_status_error; if (itp->load > 0 && !option_test(OPTION_SILENT)) { sub_context_ty *scp; scp = sub_context_new(); sub_var_set(scp, "Number", "%ld", (long)itp->load); sub_var_optional(scp, "Number"); error_intl(scp, i18n("waiting for outstanding processes")); sub_context_delete(scp); } /* * No matter what, don't do another recipe. * However, we must still wait for the * unfinished actions to complete in an orderly * fashion. */ no_persevere: assert(gp->already_recipe); walk_pos = gp->already_recipe->nrecipes * 2; continue; } /* * If there is available processing resource, and * outstanding recipe instances, run another recipe. */ trace(("itp->load = %ld;\n", (long)itp->load)); trace(("nproc = %d;\n", nproc)); trace(("walk_pos = %ld;\n", (long)walk_pos)); trace(("walk.nrecipes = %ld;\n", (long)walk.nrecipes)); while (itp->load < nproc && walk_pos < walk.nrecipes) { if (desist_requested()) goto desist; fp_sync(); /* * Extract a recipe from the list. Order does * not matter, they are all valid candidates * with up-to-date ingredients. * * However: the users expect a mostly * left-to-right order of evaluation. That * means taking the first one, NOT the last one. */ grp = walk.recipe[walk_pos++]; /* * Make sure there is no conflict with existing * single thread flags. Go hunting for a recipe * not in conflict. Come back later if we can't * find one. */ if ( grp->single_thread && string_list_intersect(grp->single_thread, &single_thread) ) { size_t k; graph_recipe_ty *kp; /* * go hunting */ for (k = walk_pos; k < walk.nrecipes; ++k) { kp = walk.recipe[k]; if ( !kp->single_thread || !string_list_intersect ( kp->single_thread, &single_thread ) ) break; } /* * Come back later if we find no alternative. */ if (k >= walk.nrecipes) { --walk_pos; break; } /* * Have the conflicting recipe and the * alternative change places. */ kp = walk.recipe[k]; trace(("k = %ld\n", (long)k)); trace(("kp = %08lX\n", (long)kp)); walk.recipe[walk_pos - 1] = kp; walk.recipe[k] = grp; grp = kp; } trace(("grp = %08lX;\n", (long)grp)); trace(("grp->input->nfiles = %ld;\n", (long)grp->input->nfiles)); trace(("grp->output->nfiles = %ld;\n", (long)grp->output->nfiles)); /* * Remember the single threading, so other * recipes avoid conflicting with *this* one. */ if (grp->single_thread) { string_list_append_list(&single_thread, grp->single_thread); } /* * run the recipe body */ run_a_recipe: status2 = func(grp, gp); /* * Look at what happened. */ if (grp->single_thread && status2 != graph_walk_status_wait) { string_list_remove_list(&single_thread, grp->single_thread); } switch (status2) { case graph_walk_status_wait: assert(itp); trace(("pid = %d;\n", graph_recipe_getpid(grp))); itab_assign(itp, graph_recipe_getpid(grp), grp); trace(("itp->load = %ld;\n", (long)itp->load)); break; case graph_walk_status_error: /* * It failed. Don't do anything with the * outputs of the recipe. Usually, we stop * altogether. */ trace(("error\n")); status = graph_walk_status_error; if (!option_test(OPTION_PERSEVERE)) goto no_persevere; break; case graph_walk_status_done_stop: /* * It worked, but we need to stop for * some reason. This is usually only * used by isit_uptodate. */ trace(("done_stop\n")); if (status == graph_walk_status_uptodate) status = graph_walk_status_done_stop; assert(itp->load == 0); goto done; case graph_walk_status_uptodate: star_as_specified('#'); /* fall through... */ case graph_walk_status_uptodate_done: /* * It worked. Now push all of the * recipes which depend on the outputs * of this recipe. */ implications_of_recipe(&walk, grp, 1); break; case graph_walk_status_done: /* * It worked. Now push all of the * recipes which depend on the outputs * of this recipe. */ implications_of_recipe(&walk, grp, 0); if (status == graph_walk_status_uptodate) status = graph_walk_status_done; break; } #ifdef HAVE_WAIT3 /* * We want to see if any children have exited. * Do not block (that's why wait3 is used). * Only do one at a time. */ if (itp->load > 0) { int pid; int exit_status; struct rusage ru; trace(("mark\n")); pid = os_wait3(&exit_status, WNOHANG, &ru); trace(("pid = %d\n", pid)); if (pid < 0) { sub_context_ty *scp; if (errno == EINTR) continue; scp = sub_context_new(); sub_errno_set(scp); fatal_intl(scp, i18n("wait(): $errno")); /* NOTREACHED */ } else if (pid > 0) { trace(("es = 0x%04X\n", exit_status)); grp = itab_query(itp, pid); if (grp) { /* if it's one of ours... */ trace(("...waited\n")); assert(pid == graph_recipe_getpid(grp)); if (grp->ocp->meter_p) grp->ocp->meter_p->ru = ru; graph_recipe_waited(grp, exit_status); itab_delete(itp, pid); trace(("itp->load = %ld;\n", (long)itp->load)); goto run_a_recipe; } } } #endif /* HAVE_WAIT3 */ } /* * Collect the results of execution, and kick off the * recipes that are blocked. Only do one at a time. */ if (itp->load > 0) { int pid; int exit_status; #ifdef HAVE_WAIT3 struct rusage ru; #endif trace(("mark\n")); #ifdef HAVE_WAIT3 pid = os_wait3(&exit_status, 0, &ru); #else pid = os_wait(&exit_status); #endif trace(("pid = %d\n", pid)); assert(pid != 0); if (pid == 0) errno = EINVAL; if (pid <= 0) { sub_context_ty *scp; if (errno == EINTR) continue; scp = sub_context_new(); sub_errno_set(scp); fatal_intl(scp, i18n("wait(): $errno")); /* NOTREACHED */ } trace(("es = 0x%04X\n", exit_status)); grp = itab_query(itp, pid); if (grp) { /* if it's one of ours... */ trace(("...waited\n")); assert(pid == graph_recipe_getpid(grp)); #ifdef HAVE_WAIT3 if (grp->ocp->meter_p) grp->ocp->meter_p->ru = ru; #endif graph_recipe_waited(grp, exit_status); itab_delete(itp, pid); trace(("itp->load = %ld;\n", (long)itp->load)); goto run_a_recipe; } } trace(("mark\n")); } done: itab_free(itp); /* * Confirmation for the user when things go wrong. */ if (status == graph_walk_status_error) symtab_walk(gp->already, excuse_me, gp); /* * Free up the list of recipes (which have been) / (to be) walked. */ graph_recipe_list_nrc_destructor(&walk); string_list_destructor(&single_thread); trace(("return %s;\n", graph_walk_status_name(status))); trace(("}\n")); return status; }
static void implications_of_file(graph_recipe_list_nrc_ty *walk, graph_file_ty *gfp, int uptodate) { size_t k; graph_recipe_ty *grp2; trace(("implications_of_file(walk=%08lX, gfp=%08lX)\n{\n", (long)walk, (long)gfp)); gfp->input_satisfied++; if (uptodate) gfp->input_uptodate++; trace(("walked \"%s\" file (%ldth of %ld recipes)\n", gfp->filename->str_text, (long)gfp->input_satisfied, (long)gfp->input->nrecipes)); if (gfp->input_satisfied < gfp->input->nrecipes) { if (!uptodate) { /* * If a file is constructed by more than one * recipe, the mod-time will be updated, making * the rest of the recipes (potentially) decide * they have nothing to do. Set their "force" * flag to make sure they do something. */ for (k = 0; k < gfp->input->nrecipes; ++k) { grp2 = gfp->input->recipe[k]; assert(grp2); grp2->multi_forced = 1; } } trace(("...come back later\n")); trace(("}\n")); return; } /* * All of the input to the file are * satisfied. Are all of the inputs to * the implied recipes satisfied? */ trace(("check %ld recipe outputs\n", (long)gfp->output->nrecipes)); for (k = 0; k < gfp->output->nrecipes; ++k) { grp2 = gfp->output->recipe[k]; assert(grp2); grp2->input_satisfied++; if (gfp->input_uptodate == gfp->input_satisfied) grp2->input_uptodate++; trace(("walked %s:%d recipe (%ldth of %ld files)\n", (grp2->rp->pos.pos_name ? grp2->rp->pos.pos_name->str_text : ""), (int)grp2->rp->pos.pos_line, (long)grp2->input_satisfied, (long)grp2->input->nfiles)); if (grp2->input_satisfied < grp2->input->nfiles) { trace(("...come back later\n")); continue; } /* * You always push the recipe, even if you think * everything is up-to-date. It may have a * use-clause, it may be a script or some other * traversal. Always push. */ trace(("recipe ingredients satisfied, push %08lX\n", (long)grp2)); graph_recipe_list_nrc_append(walk, grp2); } /* * Print a nice warm fuzzy message when a top-level target * needs no work. */ if ( gfp->primary_target && gfp->input_uptodate >= gfp->input->nrecipes && !option_test(OPTION_SILENT) ) { sub_context_ty *scp; scp = sub_context_new(); sub_var_set_string(scp, "File_Name", gfp->filename); error_intl(scp, i18n("$filename: already up to date")); sub_context_delete(scp); } trace(("}\n")); }