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 |