Revision: 1.6, Sat Nov 20 08:24:12 2004 UTC (7 months, 1 week ago) by dossy
Branch: MAIN
CVS Tags: HEAD
Changes since 1.5: +27 -26 lines
Option parsing needed to be more careful, leading to segfaults when a
bad command like [ns_schedule_proc -thread -only] is executed.  Closes
SF Bug #1068836.
/*
 * The contents of this file are subject to the AOLserver Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://aolserver.com/.
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is AOLserver Code and related documentation
 * distributed by AOL.
 * 
 * The Initial Developer of the Original Code is America Online,
 * Inc. Portions created by AOL are Copyright (C) 1999 America Online,
 * Inc. All Rights Reserved.
 *
 * Alternatively, the contents of this file may be used under the terms
 * of the GNU General Public License (the "GPL"), in which case the
 * provisions of GPL are applicable instead of those above.  If you wish
 * to allow use of your version of this file only under the terms of the
 * GPL and not to allow others to use your version of this file under the
 * License, indicate your decision by deleting the provisions above and
 * replace them with the notice and other provisions required by the GPL.
 * If you do not delete the provisions above, a recipient may use your
 * version of this file under either the License or the GPL.
 */


/*
 * tclsched.c --
 *
 *	Implement scheduled procs in Tcl. 
 */

static const char *RCSID = "@(#) $Header: /cvsroot/aolserver/aolserver/nsd/tclsched.c,v 1.6 2004/11/20 08:24:12 dossy Exp $, compiled: " __DATE__ " " __TIME__;

#include "nsd.h"

typedef void *(AtProc)(Ns_Callback *, void *);

typedef struct {
    char *server;
    char *script;
} Callback;

/*
 * Local functions defined in this file
 */

static Callback *NewCallback(Tcl_Interp *interp, char *proc, char *arg);
static Ns_Callback EvalCallback;
static Ns_Callback FreeCallback;
static Ns_SchedProc FreeSched;
static int ReturnValidId(Tcl_Interp *interp, int id, Callback *cbPtr);
static int AtCmd(AtProc *procPtr, Tcl_Interp *interp, int argc, char **argv);


/*
 *----------------------------------------------------------------------
 *
 * NsTclAt --
 *
 *	Implements ns_atsignal, ns_atshutdown, and ns_atexit commands.
 *
 * Results:
 *	Tcl result. 
 *
 * Side effects:
 *	See docs. 
 *
 *----------------------------------------------------------------------
 */

static int
AtCmd(AtProc *procPtr, Tcl_Interp *interp, int argc, char **argv)
{
    Callback *cbPtr;

    if (argc != 2 && argc != 3) {
        Tcl_AppendResult(interp, "wrong # args: should be \"",
            argv[0], " script | procname ?arg?\"", NULL);
        return TCL_ERROR;
    }
    cbPtr = NewCallback(interp, argv[1], argv[2]);
    if (procPtr == Ns_RegisterAtSignal) {
    	(*procPtr) (NsTclSignalProc, cbPtr);
    } else {
    	(*procPtr) (NsTclCallback, cbPtr);
    }
    return TCL_OK;
}
    
int
NsTclAtSignalCmd(ClientData arg, Tcl_Interp *interp, int argc, char **argv)
{
    return AtCmd(Ns_RegisterAtSignal, interp, argc, argv);
}

int
NsTclAtShutdownCmd(ClientData arg, Tcl_Interp *interp, int argc, char **argv)
{
    return AtCmd(Ns_RegisterShutdown, interp, argc, argv);
}

int
NsTclAtExitCmd(ClientData arg, Tcl_Interp *interp, int argc, char **argv)
{
    return AtCmd(Ns_RegisterAtExit, interp, argc, argv);
}


/*
 *----------------------------------------------------------------------
 *
 * NsAfterCmd --
 *
 *	Implements ns_after.
 *
 * Results:
 *	Tcl result. 
 *
 * Side effects:
 *	See docs. 
 *
 *----------------------------------------------------------------------
 */

int
NsTclAfterCmd(ClientData arg, Tcl_Interp *interp, int argc, char **argv)
{
    int id, seconds;
    Callback *cbPtr;

    if (argc != 3) {
	Tcl_AppendResult(interp, "wrong # args: should be \"",
	    argv[0], " seconds script\"", NULL);
	return TCL_ERROR;
    }
    if (Tcl_GetInt(interp, argv[1], &seconds) != TCL_OK) {
	return TCL_ERROR;
    }
    cbPtr = NewCallback(interp, argv[2], NULL);
    id = Ns_After(seconds, (Ns_Callback *) NsTclSchedProc, cbPtr, FreeCallback);
    return ReturnValidId(interp, id, cbPtr);
}
   

/*
 *----------------------------------------------------------------------
 *
 * SchedCmd --
 *
 *	Implements ns_unschedule_proc, ns_cancel, ns_pause, and
 *	ns_resume commands.
 *
 * Results:
 *	Tcl result. 
 *
 * Side effects:
 *	See docs. 
 *
 *----------------------------------------------------------------------
 */

static int
SchedCmd(Tcl_Interp *interp, int argc, char **argv, int cmd)
{
    int id, ok;
    char buf[10];

    if (argc != 2) {
        Tcl_AppendResult(interp, "wrong # args: should be \"",
            argv[0], " id\"", NULL);
        return TCL_ERROR;
    }
    if (Tcl_GetInt(interp, argv[1], &id) != TCL_OK) {
        return TCL_ERROR;
    }
    switch (cmd) {
    case 'u':
    case 'c':
    	ok = Ns_Cancel(id);
	break;
    case 'p':
    	ok = Ns_Pause(id);
	break;
    case 'r':
    	ok = Ns_Resume(id);
	break;
    default:
	ok = -1;
    }
    if (cmd != 'u') {
    	sprintf(buf, "%d", ok);
	Tcl_SetResult(interp, buf, TCL_VOLATILE);
    }
    return TCL_OK;
}

int
NsTclCancelCmd(ClientData arg, Tcl_Interp *interp, int argc, char **argv)
{
    return SchedCmd(interp, argc, argv, 'c');
}

int
NsTclPauseCmd(ClientData arg, Tcl_Interp *interp, int argc, char **argv)
{
    return SchedCmd(interp, argc, argv, 'p');
}

int
NsTclResumeCmd(ClientData arg, Tcl_Interp *interp, int argc, char **argv)
{
    return SchedCmd(interp, argc, argv, 'r');
}

int
NsTclUnscheduleCmd(ClientData arg, Tcl_Interp *interp, int argc, char **argv)
{
    return SchedCmd(interp, argc, argv, 'u');
}


/*
 *----------------------------------------------------------------------
 *
 * NsTclSchedDailyCmd --
 *
 *	Implements ns_schedule_daily. 
 *
 * Results:
 *	Tcl result. 
 *
 * Side effects:
 *	See docs. 
 *
 *----------------------------------------------------------------------
 */

int
NsTclSchedDailyCmd(ClientData arg, Tcl_Interp *interp, int argc, char **argv)
{
    Callback    *cbPtr;
    int          flags;
    int          first;
    int          id;
    int          hour, minute;

    /* 12 cases (arg count & number after cmd and -options are handled):
     *      0    1        2        3         4         5          6
     *   * cmd hour     minute   script                              (4 args)/3
     *   * cmd hour     minute   procname                            (4 args)/3
     *   * cmd hour     minute   procname  arg                       (5 args)/4
     *   * cmd -once    hour     minute    script                    (5 args)/3
     *   * cmd -once    hour     minute    procname                  (5 args)/3
     *   * cmd -once    hour     minute    procname  arg             (6 args)/4
     *   * cmd -thread  hour     minute    script                    (5 args)/3
     *   * cmd -thread  hour     minute    procname                  (5 args)/3
     *   * cmd -thread  hour     minute    procname  arg             (6 args)/4
     *   * cmd -once    -thread  hour      minute    script          (6 args)/3
     *   * cmd -once    -thread  hour      minute    procname        (6 args)/3
     *   * cmd -once    -thread  hour      minute    procname  arg   (7 args)/4
     */

    first = 1;
    flags = 0;

    while (argc-- && argv[first] != NULL) {
        if (strcmp(argv[first], "-thread") == 0) {
            flags |= NS_SCHED_THREAD;
        } else if (strcmp(argv[first], "-once") == 0) {
            flags |= NS_SCHED_ONCE;
        } else {
	    break;
        }
	first++;
    }

    if (argc < 3 || argc > 4) {
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
			 " ?-once? ?-thread? hour minute "
			 "{ script | procname ?arg? }\"", (char *) NULL);
        return TCL_ERROR;
    }


    /*
     * First is now the first argument that is not a switch.
     */

    if (Tcl_GetInt(interp, argv[first++], &hour) != TCL_OK) {
        return TCL_ERROR;
    }
    if (hour < 0 || hour > 23) {
        Tcl_AppendResult(interp, "invalid hour \"", argv[first - 1],
			 "\": should be >= 0 and <= 23", NULL);
        return TCL_ERROR;
    }

    if (Tcl_GetInt(interp, argv[first++], &minute) != TCL_OK) {
        return TCL_ERROR;
    }
    if (minute < 0 || minute > 59) {
        Tcl_AppendResult(interp, "invalid minute \"", argv[first - 1],
			 "\": should be >= 0 and <= 59", NULL);
        return TCL_ERROR;
    }

    /*
     * Bear in mind that argc has been changed when counting switches,
     * so assume that there are no switches when reading the 4 here.
     */

    cbPtr = NewCallback(interp, argv[first], argv[first+1]);
    id = Ns_ScheduleDaily(NsTclSchedProc, cbPtr, flags,
			  hour, minute, FreeSched);
    return ReturnValidId(interp, id, cbPtr);
}


/*
 *----------------------------------------------------------------------
 *
 * NsTclSchedWeeklyCmd --
 *
 *	Implements ns_sched_weekly.
 *
 * Results:
 *	Tcl result. 
 *
 * Side effects:
 *	See docs. 
 *
 *----------------------------------------------------------------------
 */

int
NsTclSchedWeeklyCmd(ClientData arg, Tcl_Interp *interp, int argc,
		     char **argv)
{
    Callback    *cbPtr;
    int          flags;
    int          first;
    int          id;
    int          day, hour, minute;

    /* 12 cases (arg count & number after cmd and -options are handled):
     *     0    1        2        3        4     5       6      7
     *  * cmd day      hour     minute  script                       (5 args)/4
     *  * cmd day      hour     minute  proc                         (5 args)/4
     *  * cmd day      hour     minute  proc    arg                  (6 args)/5
     *  * cmd -once    day      hour    minute  script               (6 args)/4
     *  * cmd -once    day      hour    minute  proc                 (6 args)/4
     *  * cmd -once    day      hour    minute  proc    arg          (7 args)/5
     *  * cmd -thread  day      hour    minute  script               (6 args)/4
     *  * cmd -thread  day      hour    minute  proc                 (6 args)/4
     *  * cmd -thread  day      hour    minute  proc    arg          (7 args)/5
     *  * cmd -once    -thread  day     hour    minute  script       (7 args)/4
     *  * cmd -once    -thread  day     hour    minute  proc         (7 args)/4
     *  * cmd -once    -thread  day     hour    minute  proc    arg  (8 args)/5
     */

    first = 1;
    flags = 0;

    while (argc-- && argv[first] != NULL) {
        if (strcmp(argv[first], "-thread") == 0) {
            flags |= NS_SCHED_THREAD;
        } else if (strcmp(argv[first], "-once") == 0) {
            flags |= NS_SCHED_ONCE;
        } else {
	    break;
        }
        first++;
    }

    if (argc < 4 || argc > 5) {
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
			 " ?-once? ?-thread? day hour minute "
			 "{ script | procname ?arg? }\"", (char *) NULL);
        return TCL_ERROR;
    }


    /*
     * First is now the first argument that is not a switch.
     */

    if (Tcl_GetInt(interp, argv[first++], &day) != TCL_OK) {
        return TCL_ERROR;
    }
    if (day < 0 || day > 6) {
        Tcl_AppendResult(interp, "invalid day \"", argv[first - 1],
			 "\": should be >= 0 and <= 6", NULL);
        return TCL_ERROR;
    }

    if (Tcl_GetInt(interp, argv[first++], &hour) != TCL_OK) {
        return TCL_ERROR;
    }
    if (hour < 0 || hour > 23) {
        Tcl_AppendResult(interp, "invalid hour \"", argv[first - 1],
			 "\": should be >= 0 and <= 23", NULL);
        return TCL_ERROR;
    }

    if (Tcl_GetInt(interp, argv[first++], &minute) != TCL_OK) {
        return TCL_ERROR;
    }
    if (minute < 0 || minute > 59) {
        Tcl_AppendResult(interp, "invalid minute \"", argv[first - 1],
			 "\": should be >= 0 and <= 59", NULL);
        return TCL_ERROR;
    }
    cbPtr = NewCallback(interp, argv[first], argv[first+1]);
    id = Ns_ScheduleWeekly(NsTclSchedProc, cbPtr, flags,
    	    	    	   day, hour, minute, FreeSched);
    return ReturnValidId(interp, id, cbPtr);
}


/*
 *----------------------------------------------------------------------
 *
 * NsTclSchedCmd --
 *
 *	Implements ns_schedule_proc. 
 *
 * Results:
 *	Tcl result. 
 *
 * Side effects:
 *	See docs. 
 *
 *----------------------------------------------------------------------
 */

int
NsTclSchedCmd(ClientData arg, Tcl_Interp *interp, int argc, char **argv)
{
    Callback	*cbPtr;
    int          interval;
    int          flags;
    int          first;
    int          id;

    /* 12 cases (arg count & number after cmd and -options are handled):
     *      0    1        2          3         4
     *   * cmd interval script                               (3 args)/2
     *   * cmd interval procname                             (3 args)/2
     *   * cmd interval procname  arg                        (4 args)/3
     *   * cmd -once    interval  script                     (4 args)/2
     *   * cmd -once    interval  procname                   (4 args)/2
     *   * cmd -once    interval  procname  arg              (5 args)/3
     *   * cmd -thread  interval  script                     (4 args)/2
     *   * cmd -thread  interval  procname                   (4 args)/2
     *   * cmd -thread  interval  procname  arg              (5 args)/3
     *   * cmd -once    -thread   interval  script           (5 args)/2
     *   * cmd -once    -thread   interval  procname         (5 args)/2
     *   * cmd -once    -thread   interval  procname arg     (6 args)/3
     */

    first = 1;
    flags = 0;

    while (argc-- && argv[first] != NULL) {
        if (strcmp(argv[first], "-thread") == 0) {
            flags |= NS_SCHED_THREAD;
        } else if (strcmp(argv[first], "-once") == 0) {
            flags |= NS_SCHED_ONCE;
        } else {
	    break;
	}
        first++;
    }

    if (argc < 2 || argc > 3) {
        Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
                " ?-once? ?-thread? interval { script | procname ?arg? }\"", 
                (char *) NULL);
        return TCL_ERROR;
    }

    /*
     * First is now the first argument that is not a switch.
     */

    if (Tcl_GetInt(interp, argv[first++], &interval) != TCL_OK) {
        return TCL_ERROR;
    }
    cbPtr = NewCallback(interp, argv[first], argv[first+1]);
    id = Ns_ScheduleProcEx(NsTclSchedProc, cbPtr, flags, interval, FreeSched);
    return ReturnValidId(interp, id, cbPtr);
}


/*
 *----------------------------------------------------------------------
 *
 * ReturnValidId --
 *
 *	Update the interp result with the given schedule id if valid.
 *	Otherwise, free the script and leave an error in the interp.
 *
 * Results:
 *	TCL_OK or TCL_ERROR.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static int
ReturnValidId(Tcl_Interp *interp, int id, Callback *cbPtr)
{
    char buf[10];

    if (id == NS_ERROR) {
	Tcl_SetResult(interp, "could not schedule procedure", TCL_STATIC);
	FreeCallback(cbPtr);
        return TCL_ERROR;
    }
    sprintf(buf, "%d", id);
    Tcl_SetResult(interp, buf, TCL_VOLATILE);
    return TCL_OK;
}


/*
 *----------------------------------------------------------------------
 *
 * EvalCallback --
 *
 *	This is a callback function that runs scheduled tcl 
 *	procedures registered with ns_schedule_*. 
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	Will run a Tcl proc/script. 
 *
 *----------------------------------------------------------------------
 */

static void
EvalCallback(void *arg)
{
    Callback *cbPtr = arg;
    
    (void) Ns_TclEval(NULL, cbPtr->server, cbPtr->script);
}


/*
 *----------------------------------------------------------------------
 *
 * NewCallback --
 *
 *	Create a new script callback.
 *
 * Results:
 *	Pointer to Callback.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static Callback *
NewCallback(Tcl_Interp *interp, char *proc, char *arg)
{
    Callback *cbPtr;
    char *argv[2];

    argv[0] = proc;
    argv[1] = arg;
    cbPtr = ns_malloc(sizeof(Callback));
    cbPtr->server = Ns_TclInterpServer(interp);
    cbPtr->script = Tcl_Concat(arg ? 2 : 1, argv);
    return cbPtr;
}


/*
 *----------------------------------------------------------------------
 *
 * FreeCallback --
 *
 *	Free a Callback created with NewCallback.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static void
FreeCallback(void *arg)
{
    Callback *cbPtr = arg;

    ckfree(cbPtr->script);
    ns_free(cbPtr);
}

static void
FreeSched(void *arg, int id)
{
    FreeCallback(arg);
}


/*
 *----------------------------------------------------------------------
 *
 * NsTclSchedProc, NsTclSignalProc, NsTclCallback --
 *
 *	External wrapper for various Tcl callbacks.
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

void
NsTclSchedProc(void *arg, int id)
{
    EvalCallback(arg);
}

void
NsTclSignalProc(void *arg)
{
    EvalCallback(arg);
}

void
NsTclCallback(void *arg)
{
    EvalCallback(arg);
    FreeCallback(arg);
}


/*
 *----------------------------------------------------------------------
 *
 * NsTclArgProc --
 *
 *	Proc info routine to copy Tcl callback script.
 *
 * Results:
 *	None. 
 *
 * Side effects:
 *	Will copy script to given dstring.
 *
 *----------------------------------------------------------------------
 */

void
NsTclArgProc(Tcl_DString *dsPtr, void *arg)
{
     Callback *cbPtr = arg;

     Tcl_DStringAppendElement(dsPtr, cbPtr->script);
}

Back to SourceForge.net

Powered by ViewCVS 1.0-dev