Exemple #1
0
/*
    Get the time to sleep till the next pending event. Must be called locked.
 */
static MprTicks getIdleTicks(MprEventService *es, MprTicks timeout)
{
    MprDispatcher   *readyQ, *waitQ, *dp;
    MprEvent        *event;
    MprTicks        delay;

    waitQ = es->waitQ;
    readyQ = es->readyQ;

    if (readyQ->next != readyQ) {
        delay = 0;
    } else if (mprIsStopping()) {
        delay = 10;
    } else {
        /*
            Examine all the dispatchers on the waitQ
         */
        delay = es->delay ? es->delay : MPR_MAX_TIMEOUT;
        for (dp = waitQ->next; dp != waitQ; dp = dp->next) {
            event = dp->eventQ->next;
            if (event != dp->eventQ) {
                delay = min(delay, (event->due - es->now));
                if (delay <= 0) {
                    break;
                }
            }
        }
        delay = min(delay, timeout);
        es->delay = 0;
    }
    return delay < 0 ? 0 : delay;
}
Exemple #2
0
/*  
    static function run(timeout: Number = -1, oneEvent: Boolean = false): Boolean
 */
static EjsObj *app_run(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv)
{
    MprTicks    mark, remaining;
    int64       dispatcherMark;
    int         rc, oneEvent, timeout;

    timeout = (argc > 0) ? ejsGetInt(ejs, argv[0]) : MAXINT;
    oneEvent = (argc > 1) ? ejsGetInt(ejs, argv[1]) : 0;

    if (ejs->hosted) {
        return ESV(true);
    }
    if (timeout < 0) {
        timeout = MAXINT;
    }
    mark = mprGetTicks();
    remaining = timeout;
    dispatcherMark = mprGetEventMark(ejs->dispatcher);
    do {
        rc = mprWaitForEvent(ejs->dispatcher, remaining, dispatcherMark); 
        remaining = mprGetRemainingTicks(mark, timeout);
        dispatcherMark = mprGetEventMark(ejs->dispatcher);
    } while (!ejs->exception && !oneEvent && !ejs->exiting && remaining > 0 && !mprIsStopping());
    return (rc == 0) ? ESV(true) : ESV(false);
}
Exemple #3
0
/*  
    function run(): Void
 */
static EjsVoid *hs_run(Ejs *ejs, EjsHttpServer *sp, int argc, EjsObj **argv)
{
    if (!sp->hosted) {
        while (!ejs->exiting && !mprIsStopping()) {
            mprWaitForEvent(ejs->dispatcher, MAXINT); 
        }
    }
    return 0;
}
Exemple #4
0
/*
    function run(): Void
 */
static EjsVoid *hs_run(Ejs *ejs, EjsHttpServer *sp, int argc, EjsObj **argv)
{
    int64   dispatcherMark;

    if (!sp->hosted) {
        dispatcherMark = mprGetEventMark(ejs->dispatcher);
        while (!ejs->exiting && !mprIsStopping()) {
            mprWaitForEvent(ejs->dispatcher, MPR_MAX_TIMEOUT, dispatcherMark);
            dispatcherMark = mprGetEventMark(ejs->dispatcher);
        }
    }
    return 0;
}
Exemple #5
0
static int unloadEsp(MprModule *mp)
{
    HttpStage   *stage;

    if (esp->inUse) {
       return MPR_ERR_BUSY;
    }
    if (mprIsStopping()) {
        return 0;
    }
    if ((stage = httpLookupStage(mp->name)) != 0) {
        stage->flags |= HTTP_STAGE_UNLOADED;
    }
    return 0;
}
Exemple #6
0
/*  
    Interpret from the console or from a literal command
 */
static int interpretCommands(EcCompiler *cp, cchar *cmd)
{
    Ejs         *ejs;
    EjsString   *result;
    char        *tmpArgv[1];
    int         err;

    ejs = cp->ejs;
    cp->interactive = 1;

    if (ecOpenConsoleStream(cp, (cmd) ? commandGets: consoleGets, cmd) < 0) {
        mprError("Cannot open input");
        return EJS_ERR;
    }
    tmpArgv[0] = EC_INPUT_STREAM;

    while (!cp->stream->eof && !mprIsStopping()) {
        err = 0;
        cp->uid = 0;
        ejs->result = ESV(undefined);
        if (ecCompile(cp, 1, tmpArgv) < 0) {
            mprRawLog(0, "%s", cp->errorMsg);
            ejs->result = ESV(undefined);
            err++;
        }
        if (!err && cp->errorCount == 0) {
            if (ejsRunProgram(ejs, NULL, NULL) < 0) {
                ejsReportError(ejs, "Error in script");
            }
        }
        if (!ejs->exception && ejs->result != ESV(undefined)) {
            if (ejsIs(ejs, ejs->result, Date) /* MOB || ejsIsType(ejs, ejs->result) */) {
                if ((result = (EjsString*) ejsToString(ejs, ejs->result)) != 0) {
                    mprPrintf("%@\n", result);
                }
            } else if (ejs->result != ESV(null)) {
                if ((result = (EjsString*) ejsSerialize(ejs, ejs->result, EJS_JSON_SHOW_PRETTY)) != 0) {
                    mprPrintf("%@\n", result);
                }
            }
        }
        ecResetInput(cp);
        cp->errorCount = 0;
        cp->fatalError = 0;
    }
    ecCloseStream(cp);
    return 0;
}
Exemple #7
0
/*  
    Pause the application. This services events while asleep.
    static function sleep(delay: Number = -1): void
    MOB - sleep currently throws if an exception is generated in an event callback (worker).
    It should not.
 */
static EjsObj *app_sleep(Ejs *ejs, EjsObj *unused, int argc, EjsObj **argv)
{
    MprTicks    mark, remaining;
    int         timeout;

    timeout = (argc > 0) ? ejsGetInt(ejs, argv[0]) : MAXINT;
    if (timeout < 0) {
        timeout = MAXINT;
    }
    mark = mprGetTicks();
    remaining = timeout;
    do {
        mprWaitForEvent(ejs->dispatcher, (int) remaining); 
        remaining = mprGetRemainingTicks(mark, timeout);
    } while (!ejs->exiting && remaining > 0 && !mprIsStopping());
    return 0;
}
Exemple #8
0
/*
    Sample main event loop. This demonstrates how to integrate Mpr with your
    applications event loop using select()
 */
void eventLoop()
{
    MSG     msg;

    /*
        If single threaded or if you desire control over the event loop, you 
        should code an event loop similar to that below:
     */
    while (!mprIsStopping()) {		
        /*
            Socket events will be serviced in the msgProc
         */
        if (GetMessage(&msg, NULL, 0, 0) == 0) {
            /*  WM_QUIT received */
            break;
        }
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}
Exemple #9
0
/*
    Schedule events.
    This routine will service events until the timeout expires or if MPR_SERVICE_NO_BLOCK is specified in flags,
    until there are no more events to service. This routine will also return when the MPR is stopping. This will
    service all enabled non-running dispatcher queues and pending I/O events.
    An app should dedicate only one thread to be an event service thread.
    @param timeout Time in milliseconds to wait. Set to zero for no wait. Set to -1 to wait forever.
    @param flags Set to MPR_SERVICE_NO_BLOCK for non-blocking.
    @returns Number of events serviced.
 */
PUBLIC int mprServiceEvents(MprTicks timeout, int flags)
{
    MprEventService     *es;
    MprDispatcher       *dp;
    MprTicks            expires, delay;
    int                 beginEventCount, eventCount;

    if (MPR->eventing) {
        mprLog("warn mpr event", 0, "mprServiceEvents called reentrantly");
        return 0;
    }
    mprAtomicBarrier();
    if (mprIsDestroying()) {
        return 0;
    }
    MPR->eventing = 1;
    es = MPR->eventService;
    beginEventCount = eventCount = es->eventCount;
    es->now = mprGetTicks();
    expires = timeout < 0 ? MPR_MAX_TIMEOUT : (es->now + timeout);
    if (expires < 0) {
        expires = MPR_MAX_TIMEOUT;
    }
    mprSetWindowsThread(0);

    while (es->now <= expires) {
        eventCount = es->eventCount;
        mprServiceSignals();

        while ((dp = getNextReadyDispatcher(es)) != NULL) {
            assert(!isRunning(dp));
            queueDispatcher(es->runQ, dp);
            if (dp->flags & MPR_DISPATCHER_IMMEDIATE) {
                dispatchEventsWorker(dp);
            } else {
                if (mprStartWorker((MprWorkerProc) dispatchEventsWorker, dp) < 0) {
                    /* Should not get here */
                    queueDispatcher(es->pendingQ, dp);
                    break;
                }
            }
        }
        if (flags & MPR_SERVICE_NO_BLOCK) {
            expires = 0;
            /* But still service I/O events below */
        }
        if (es->eventCount == eventCount) {
            lock(es);
            delay = getIdleTicks(es, expires - es->now);
            es->willAwake = es->now + delay;
            es->waiting = 1;
            unlock(es);
            /*
                Service IO events
             */
            mprWaitForIO(MPR->waitService, delay);
        }
        es->now = mprGetTicks();
        if (flags & MPR_SERVICE_NO_BLOCK) {
            break;
        }
        if (mprIsStopping()) {
            break;
        }
    }
    MPR->eventing = 0;
    mprSignalCond(MPR->cond);
    return abs(es->eventCount - beginEventCount);
}
Exemple #10
0
static void runService()
{
    MprTime     mark;
    char        **av, *env[3], **argv;
    int         err, i, status, ac, next;

    app->servicePid = 0;
    atexit(cleanup);

    mprLog(1, "%s: Watching over %s", app->appName, app->serviceProgram);

    if (access(app->serviceProgram, X_OK) < 0) {
        mprError("start: can't access %s, errno %d", app->serviceProgram, mprGetOsError());
        return;
    }
    if (writePid(getpid()) < 0) {
        return;
    }
    mark = mprGetTime();

    while (!mprIsStopping()) {
        if (mprGetElapsedTime(mark) > (3600 * 1000)) {
            mark = mprGetTime();
            app->restartCount = 0;
            app->restartWarned = 0;
        }
        if (app->servicePid == 0) {
            if (app->restartCount >= app->retries) {
                if (! app->restartWarned) {
                    mprError("Too many restarts for %s, %d in ths last hour", app->serviceProgram, app->restartCount);
                    mprError("Suspending restarts for one hour");
                    app->restartWarned++;
                }
                mprSleep(60 * 1000);
                continue;
            }

            /*
                Create the child
             */
            app->servicePid = vfork();
            if (app->servicePid < 0) {
                mprError("Can't fork new process to run %s", app->serviceProgram);
                continue;

            } else if (app->servicePid == 0) {
                /*
                    Child
                 */
                umask(022);
                setsid();

                mprLog(1, "%s: Change dir to %s", app->appName, app->serviceHome);
                if (chdir(app->serviceHome) < 0) {}

                for (i = 3; i < 128; i++) {
                    close(i);
                }
                if (app->serviceArgs && *app->serviceArgs) {
                    ac = mprMakeArgv(app->serviceArgs, &av, 0);
                } else {
                    ac = 0;
                }
                argv = mprAlloc(sizeof(char*) * (6 + ac));
                env[0] = sjoin("LD_LIBRARY_PATH=", app->serviceHome, NULL);
                env[1] = sjoin("PATH=", getenv("PATH"), NULL);
                env[2] = 0;

                next = 0;
                argv[next++] = app->serviceProgram;
                if (app->logSpec) {
                    argv[next++] = "--log";
                    argv[next++] = (char*) app->logSpec;
                }
                for (i = 0; i < ac; i++) {
                    argv[next++] = av[i];
                }
                argv[next++] = 0;

                mprLog(1, "%s: Running %s", app->appName, app->serviceProgram);
                for (i = 1; argv[i]; i++) {
                    mprLog(1, "%s: argv[%d] = %s", app->appName, i, argv[i]);
                }
                execve(app->serviceProgram, argv, (char**) &env);

                /* Should not get here */
                err = errno;
                mprError("%s: Can't exec %s, err %d, cwd %s", app->appName, app->serviceProgram, err, app->serviceHome);
                exit(MPR_ERR_CANT_INITIALIZE);
            }

            /*
                Parent
             */
            mprLog(1, "%s: create child %s at pid %d", app->appName, app->serviceProgram, app->servicePid);
            app->restartCount++;

            waitpid(app->servicePid, &status, 0);
            mprLog(1, "%s: %s has exited with status %d", app->appName, app->serviceProgram, WEXITSTATUS(status));
            if (!mprIsStopping()) {
                mprLog(1, "%s: Restarting %s (%d/%d)...", 
                    app->appName, app->serviceProgram, app->restartCount, app->retries);
            }
            app->servicePid = 0;
        }
    }
}
Exemple #11
0
MAIN(appweb, int argc, char **argv, char **envp)
{
    Mpr     *mpr;
    cchar   *ipAddrPort, *argp, *jail;
    char    *ip, *logSpec;
    int     argind, port, status, verbose;

    ipAddrPort = 0;
    ip = 0;
    jail = 0;
    port = -1;
    verbose = 0;
    logSpec = 0;
    argv[0] = BLD_APPWEB_PATH;

    if ((mpr = mprCreate(argc, argv, MPR_USER_EVENTS_THREAD)) == NULL) {
        exit(1);
    }
    mprSetAppName(BLD_PRODUCT, BLD_NAME, BLD_VERSION);

    if ((app = mprAllocObj(App, manageApp)) == NULL) {
        exit(2);
    }
    mprAddRoot(app);
    mprAddStandardSignals();

#if BLD_FEATURE_ROMFS
    extern MprRomInode romFiles[];
    mprSetRomFileSystem(romFiles);
#endif

    app->mpr = mpr;
    app->workers = -1;
    app->configFile = BLD_CONFIG_FILE;
    app->home = BLD_SERVER_ROOT;
    app->documents = app->home;
    argc = mpr->argc;
    argv = mpr->argv;

    for (argind = 1; argind < argc; argind++) {
        argp = argv[argind];
        if (*argp != '-') {
            break;
        }
        if (smatch(argp, "--config") || smatch(argp, "--conf")) {
            if (argind >= argc) {
                usageError();
            }
            app->configFile = sclone(argv[++argind]);

#if BLD_UNIX_LIKE
        } else if (smatch(argp, "--chroot")) {
            if (argind >= argc) {
                usageError();
            }
            jail = mprGetAbsPath(argv[++argind]);
#endif

        } else if (smatch(argp, "--debugger") || smatch(argp, "-D")) {
            mprSetDebugMode(1);

        } else if (smatch(argp, "--exe")) {
            if (argind >= argc) {
                usageError();
            }
            mpr->argv[0] = mprGetAbsPath(argv[++argind]);
            mprSetAppPath(mpr->argv[0]);
            mprSetModuleSearchPath(NULL);

        } else if (smatch(argp, "--home")) {
            if (argind >= argc) {
                usageError();
            }
            app->home = mprGetAbsPath(argv[++argind]);
#if UNUSED && KEEP
            if (chdir(app->home) < 0) {
                mprError("%s: Can't change directory to %s", mprGetAppName(), app->home);
                exit(4);
            }
#endif

        } else if (smatch(argp, "--log") || smatch(argp, "-l")) {
            if (argind >= argc) {
                usageError();
            }
            logSpec = argv[++argind];

        } else if (smatch(argp, "--name") || smatch(argp, "-n")) {
            if (argind >= argc) {
                usageError();
            }
            mprSetAppName(argv[++argind], 0, 0);

        } else if (smatch(argp, "--threads")) {
            if (argind >= argc) {
                usageError();
            }
            app->workers = atoi(argv[++argind]);

        } else if (smatch(argp, "--verbose") || smatch(argp, "-v")) {
            verbose++;

        } else if (smatch(argp, "--version") || smatch(argp, "-V")) {
            mprPrintf("%s %s-%s\n", mprGetAppTitle(), BLD_VERSION, BLD_NUMBER);
            exit(0);

        } else {
            mprError("Unknown switch \"%s\"", argp);
            usageError();
            exit(5);
        }
    }
    if (logSpec) {
        mprStartLogging(logSpec, 1);
        mprSetCmdlineLogging(1);
    } else if (verbose) {
        mprStartLogging(sfmt("stderr:%d", verbose + 1), 1);
        mprSetCmdlineLogging(1);
    }
    if (mprStart() < 0) {
        mprUserError("Can't start MPR for %s", mprGetAppName());
        mprDestroy(MPR_EXIT_DEFAULT);
        return MPR_ERR_CANT_INITIALIZE;
    }
    if (checkEnvironment(argv[0]) < 0) {
        exit(6);
    }
    if (argc > argind) {
        if (argc > (argind + 2)) {
            usageError();
        }
        ipAddrPort = argv[argind++];
        if (argc > argind) {
            app->documents = sclone(argv[argind++]);
        }
        mprParseSocketAddress(ipAddrPort, &ip, &port, HTTP_DEFAULT_PORT);
        
    } else if (findConfigFile() < 0) {
        exit(7);
    }
    if (jail && changeRoot(jail) < 0) {
        exit(8);
    }
    if (initialize(ip, port) < 0) {
        return MPR_ERR_CANT_INITIALIZE;
    }
    if (maStartAppweb(app->appweb) < 0) {
        mprUserError("Can't start HTTP service, exiting.");
        exit(9);
    }
    /*
        Service I/O events until instructed to exit
     */
    while (!mprIsStopping()) {
        mprServiceEvents(-1, 0);
    }
    status = mprGetExitStatus();
    mprLog(1, "Stopping Appweb ...");
    maStopAppweb(app->appweb);
    mprDestroy(MPR_EXIT_DEFAULT);
    return status;
}
Exemple #12
0
/*
    The http timer does maintenance activities and will fire per second while there are active requests.
    This routine will also be called by httpTerminate with event == 0 to signify a shutdown.
    NOTE: Because we lock the http here, connections cannot be deleted while we are modifying the list.
 */
static void httpTimer(Http *http, MprEvent *event)
{
    HttpConn    *conn;
    HttpStage   *stage;
    HttpLimits  *limits;
    MprModule   *module;
    int         next, active, abort;

    updateCurrentDate();

    /* 
       Check for any inactive connections or expired requests (inactivityTimeout and requestTimeout)
       OPT - could check for expired connections every 10 seconds.
     */
    lock(http->connections);
    for (active = 0, next = 0; (conn = mprGetNextItem(http->connections, &next)) != 0; active++) {
        limits = conn->limits;
        if (!conn->timeoutEvent) {
            abort = mprIsStopping();
            if (httpServerConn(conn) && (HTTP_STATE_CONNECTED < conn->state && conn->state < HTTP_STATE_PARSED) && 
                    (http->now - conn->started) > limits->requestParseTimeout) {
                conn->timeout = HTTP_PARSE_TIMEOUT;
                abort = 1;
            } else if ((http->now - conn->lastActivity) > limits->inactivityTimeout) {
                conn->timeout = HTTP_INACTIVITY_TIMEOUT;
                abort = 1;
            } else if ((http->now - conn->started) > limits->requestTimeout) {
                conn->timeout = HTTP_REQUEST_TIMEOUT;
                abort = 1;
            } else if (!event) {
                /* Called directly from httpStop to stop connections */
                if (MPR->exitTimeout > 0) {
                    if (conn->state == HTTP_STATE_COMPLETE || 
                        (HTTP_STATE_CONNECTED < conn->state && conn->state < HTTP_STATE_PARSED)) {
                        abort = 1;
                    }
                } else {
                    abort = 1;
                }
            }
            if (abort && !mprGetDebugMode()) {
                httpScheduleConnTimeout(conn);
            }
        }
    }

    /*
        Check for unloadable modules
        OPT - could check for modules every minute
     */
    if (mprGetListLength(http->connections) == 0) {
        for (next = 0; (module = mprGetNextItem(MPR->moduleService->modules, &next)) != 0; ) {
            if (module->timeout) {
                if (module->lastActivity + module->timeout < http->now) {
                    mprLog("info http", 2, "Unloading inactive module %s", module->name);
                    if ((stage = httpLookupStage(module->name)) != 0) {
                        if (mprUnloadModule(module) < 0)  {
                            active++;
                        } else {
                            stage->flags |= HTTP_STAGE_UNLOADED;
                        }
                    } else {
                        mprUnloadModule(module);
                    }
                } else {
                    active++;
                }
            }
        }
    }
    httpPruneMonitors();

    if (active == 0 || mprIsStopping()) {
        if (event) {
            mprRemoveEvent(event);
        }
        http->timer = 0;
        /*
            Going to sleep now, so schedule a GC to free as much as possible.
         */
        mprGC(MPR_GC_FORCE | MPR_GC_NO_BLOCK);
    } else {
        mprGC(MPR_GC_NO_BLOCK);
    }
    unlock(http->connections);
}
Exemple #13
0
MAIN(appweb, int argc, char **argv, char **envp)
{
    Mpr     *mpr;
    cchar   *argp, *jail;
    char    *logSpec;
    int     argind, status, verbose;

    jail = 0;
    verbose = 0;
    logSpec = 0;

    if ((mpr = mprCreate(argc, argv, MPR_USER_EVENTS_THREAD)) == NULL) {
        exit(1);
    }
    mprSetAppName(BIT_PRODUCT, BIT_TITLE, BIT_VERSION);

    /*
        Allocate the top level application object. ManageApp is the GC manager function and is called
        by the GC to mark references in the app object.
     */
    if ((app = mprAllocObj(AppwebApp, manageApp)) == NULL) {
        exit(2);
    }
    mprAddRoot(app);
    mprAddStandardSignals();

    app->mpr = mpr;
    app->configFile = sclone("appweb.conf");
    app->home = mprGetCurrentPath();
    app->documents = app->home;
    argc = mpr->argc;
    argv = (char**) mpr->argv;

    for (argind = 1; argind < argc; argind++) {
        argp = argv[argind];
        if (*argp != '-') {
            break;
        }
        if (smatch(argp, "--config") || smatch(argp, "--conf")) {
            if (argind >= argc) {
                usageError();
            }
            app->configFile = sclone(argv[++argind]);

#if BIT_UNIX_LIKE
        } else if (smatch(argp, "--chroot")) {
            if (argind >= argc) {
                usageError();
            }
            jail = mprGetAbsPath(argv[++argind]);
#endif

        } else if (smatch(argp, "--debugger") || smatch(argp, "-D")) {
            mprSetDebugMode(1);

        } else if (smatch(argp, "--exe")) {
            if (argind >= argc) {
                usageError();
            }
            mpr->argv[0] = mprGetAbsPath(argv[++argind]);
            mprSetAppPath(mpr->argv[0]);
            mprSetModuleSearchPath(NULL);

        } else if (smatch(argp, "--home")) {
            if (argind >= argc) {
                usageError();
            }
            app->home = mprGetAbsPath(argv[++argind]);
            if (chdir(app->home) < 0) {
                mprError("%s: Cannot change directory to %s", mprGetAppName(), app->home);
                exit(4);
            }

        } else if (smatch(argp, "--log") || smatch(argp, "-l")) {
            if (argind >= argc) {
                usageError();
            }
            logSpec = argv[++argind];

        } else if (smatch(argp, "--name") || smatch(argp, "-n")) {
            if (argind >= argc) {
                usageError();
            }
            mprSetAppName(argv[++argind], 0, 0);

        } else if (smatch(argp, "--verbose") || smatch(argp, "-v")) {
            verbose++;

        } else if (smatch(argp, "--version") || smatch(argp, "-V")) {
            mprPrintf("%s-%s\n", BIT_VERSION, BIT_BUILD_NUMBER);
            exit(0);

        } else {
            if (!smatch(argp, "?")) {
                mprError("Unknown switch \"%s\"", argp);
            }
            usageError();
            exit(5);
        }
    }
    if (logSpec) {
        mprStartLogging(logSpec, 1);
        mprSetCmdlineLogging(1);

    } else if (verbose) {
        mprStartLogging(sfmt("stderr:%d", verbose + 1), 1);
        mprSetCmdlineLogging(1);
    }
    /*
        Start the multithreaded portable runtime (MPR)
     */
    if (mprStart() < 0) {
        mprError("Cannot start MPR for %s", mprGetAppName());
        mprDestroy(MPR_EXIT_DEFAULT);
        return MPR_ERR_CANT_INITIALIZE;
    }
    if (checkEnvironment(argv[0]) < 0) {
        exit(6);
    }
    if (findAppwebConf() < 0) {
        exit(7);
    }
    if (jail && changeRoot(jail) < 0) {
        exit(8);
    }
    /*
        Open the sockets to listen on
     */
    if (createEndpoints(argc - argind, &argv[argind]) < 0) {
        return MPR_ERR_CANT_INITIALIZE;
    }
    /*
        Start HTTP services
     */
    if (maStartAppweb(app->appweb) < 0) {
        mprError("Cannot start HTTP service, exiting.");
        exit(9);
    }
    /*
        Service I/O events until instructed to exit
     */
    while (!mprIsStopping()) {
        mprServiceEvents(-1, 0);
    }
    status = mprGetExitStatus();
    mprLog(1, "Stopping Appweb ...");
    maStopAppweb(app->appweb);
    mprDestroy(MPR_EXIT_DEFAULT);
    return status;
}