Example #1
0
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;
}
Example #2
0
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;
}
Example #3
0
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;
}
Example #4
0
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;
}
Example #5
0
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;
}
Example #6
0
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;
}
Example #7
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();
}
Example #8
0
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;
}
Example #9
0
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;
}
Example #10
0
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;
}
Example #11
0
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;
}
Example #12
0
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;
}
Example #13
0
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"));
}