...
 
Commits (1)
  • Rob Swindell's avatar
    Fix next-forced-exclusive event time calculation · d6a2af22
    Rob Swindell authored
    Jump the time forward (in 24-hour chunks) to find the next date/time the event will run rather than just adding 24-hours and assuming it's an event that runs every day (of the week or month) at a specific time.
    
    Also, expose the next-run-date/time for an event as a new `next_run` property for `xtrn_area.event[]` (in `time_t` format) for easier debugging of these kinds of issues.
    Also expose the error log level as a new property: `error_level` while we're here.
    d6a2af22
......@@ -133,6 +133,45 @@ int sbbs_t::getuserxfers(int fromuser, int destuser, char *fname)
return(found);
}
/****************************************************************************/
/* Return date/time that the specified event should run next */
/****************************************************************************/
extern "C" time_t DLLCALL getnexteventtime(event_t* event)
{
struct tm tm;
time_t t = time(NULL);
if(event->misc & EVENT_DISABLED)
return 0;
if(event->days == 0 || event->freq != 0)
return 0;
if(localtime_r(&t, &tm) == NULL)
return 0;
tm.tm_hour = event->time / 60;
tm.tm_min = event->time % 60;
tm.tm_sec = 0;
tm.tm_isdst = -1; /* Do not adjust for DST */
t = mktime(&tm);
if(event->last >= t)
t += 24 * 60 * 60; /* already ran today, so add 24hrs */
do {
if(localtime_r(&t, &tm) == NULL)
return 0;
if((event->days & (1 << tm.tm_wday))
&& (event->mdays == 0 || (event->mdays & (1 << tm.tm_mday)))
&& (event->months == 0 || (event->months & (1 << tm.tm_mon))))
break;
t += 24 * 60 * 60;
} while(t > 0);
return t;
}
/****************************************************************************/
/* Return time of next forced timed event */
/* 'event' may be NULL */
......@@ -140,40 +179,21 @@ int sbbs_t::getuserxfers(int fromuser, int destuser, char *fname)
extern "C" time_t DLLCALL getnextevent(scfg_t* cfg, event_t* event)
{
int i;
time_t now=time(NULL);
time_t event_time=0;
time_t thisevent;
time_t tmptime;
struct tm tm, last_tm;
if(localtime_r(&now,&tm) == NULL)
return 0;
for(i=0;i<cfg->total_events;i++) {
if(!cfg->event[i]->node || cfg->event[i]->node>cfg->sys_nodes
|| cfg->event[i]->misc&EVENT_DISABLED)
continue;
if(!(cfg->event[i]->misc&EVENT_FORCE)
|| (!(cfg->event[i]->misc&EVENT_EXCL) && cfg->event[i]->node!=cfg->node_num)
|| !(cfg->event[i]->days&(1<<tm.tm_wday))
|| (cfg->event[i]->mdays!=0 && !(cfg->event[i]->mdays&(1<<tm.tm_mday)))
|| (cfg->event[i]->months!=0 && !(cfg->event[i]->months&(1<<tm.tm_mon))))
|| (!(cfg->event[i]->misc&EVENT_EXCL) && cfg->event[i]->node!=cfg->node_num))
continue;
tm.tm_hour=cfg->event[i]->time/60;
tm.tm_min=cfg->event[i]->time%60;
tm.tm_sec=0;
tm.tm_isdst=-1; /* Do not adjust for DST */
thisevent=mktime(&tm);
if(thisevent == -1)
thisevent = getnexteventtime(cfg->event[i]);
if(thisevent <= 0)
continue;
tmptime=cfg->event[i]->last;
if(localtime_r(&tmptime,&last_tm) == NULL)
memset(&last_tm,0,sizeof(last_tm));
if(tm.tm_mday==last_tm.tm_mday && tm.tm_mon==last_tm.tm_mon)
thisevent+=24L*60L*60L; /* already ran today, so add 24hrs */
if(!event_time || thisevent<event_time) {
event_time=thisevent;
if(event!=NULL)
......
/* js_xtrn_area.c */
/* Synchronet JavaScript "External Program Area" Object */
/* $Id: js_xtrn_area.c,v 1.31 2019/01/05 06:33:39 rswindell Exp $ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
......@@ -17,21 +13,9 @@
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
......@@ -83,12 +67,14 @@ static char* event_prop_desc[] = {
"command-line"
,"startup directory"
,"node number"
,"time to execute"
,"time to execute (minutes since midnight)"
,"frequency to execute"
,"days of week to execute (bitfield)"
,"days of month to execute (bitfield)"
,"months of year to execute (bitfield)"
,"date/time last run (in time_t format)"
,"date/time of last run (in time_t format)"
,"date/time of next run (in time_t format)"
,"error log level"
,"toggle options (bitfield)"
,NULL
};
......@@ -105,6 +91,40 @@ static char* xedit_prop_desc[] = {
#endif
/* Event Object Properties */
enum {
EVENT_PROP_CMD,
EVENT_PROP_STARTUP_DIR,
EVENT_PROP_NODE_NUM,
EVENT_PROP_TIME,
EVENT_PROP_FREQ,
EVENT_PROP_DAYS,
EVENT_PROP_MDAYS,
EVENT_PROP_MONTHS,
EVENT_PROP_LAST_RUN,
EVENT_PROP_NEXT_RUN,
EVENT_PROP_ERRLEVEL,
EVENT_PROP_MISC
};
static jsSyncPropertySpec js_event_properties[] = {
/* name ,tinyid ,flags ,ver */
{ "cmd" ,EVENT_PROP_CMD ,JSPROP_ENUMERATE|JSPROP_READONLY ,311},
{ "startup_dir" ,EVENT_PROP_STARTUP_DIR ,JSPROP_ENUMERATE|JSPROP_READONLY ,311},
{ "node_num" ,EVENT_PROP_NODE_NUM ,JSPROP_ENUMERATE|JSPROP_READONLY ,311},
{ "time" ,EVENT_PROP_TIME ,JSPROP_ENUMERATE|JSPROP_READONLY ,311},
{ "freq" ,EVENT_PROP_FREQ ,JSPROP_ENUMERATE|JSPROP_READONLY ,311},
{ "days" ,EVENT_PROP_DAYS ,JSPROP_ENUMERATE|JSPROP_READONLY ,311},
{ "mdays" ,EVENT_PROP_MDAYS ,JSPROP_ENUMERATE|JSPROP_READONLY ,311},
{ "months" ,EVENT_PROP_MONTHS ,JSPROP_ENUMERATE|JSPROP_READONLY ,311},
{ "last_run" ,EVENT_PROP_LAST_RUN ,JSPROP_ENUMERATE|JSPROP_READONLY ,311},
{ "next_run" ,EVENT_PROP_NEXT_RUN ,JSPROP_ENUMERATE|JSPROP_READONLY ,31802},
{ "error_level" ,EVENT_PROP_ERRLEVEL ,JSPROP_ENUMERATE|JSPROP_READONLY ,31802},
{ "settings" ,EVENT_PROP_MISC ,JSPROP_ENUMERATE|JSPROP_READONLY ,311},
{ NULL }
};
BOOL DLLCALL js_CreateXtrnProgProperties(JSContext* cx, JSObject* obj, xtrn_t* xtrn)
{
JSString* js_str;
......@@ -182,6 +202,79 @@ BOOL DLLCALL js_CreateXtrnProgProperties(JSContext* cx, JSObject* obj, xtrn_t* x
return(TRUE);
}
static JSBool js_event_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
{
const char* p = NULL;
JSString* js_str;
jsval idval;
jsint tiny;
event_t* event;
if((event = JS_GetPrivate(cx, obj)) == NULL)
return JS_FALSE;
JS_IdToValue(cx, id, &idval);
tiny = JSVAL_TO_INT(idval);
switch(tiny) {
case EVENT_PROP_CMD:
p = event->cmd;
break;
case EVENT_PROP_STARTUP_DIR:
p = event->dir;
break;
case EVENT_PROP_NODE_NUM:
*vp = UINT_TO_JSVAL(event->node);
break;
case EVENT_PROP_TIME:
*vp = UINT_TO_JSVAL(event->time);
break;
case EVENT_PROP_FREQ:
*vp = UINT_TO_JSVAL(event->freq);
break;
case EVENT_PROP_DAYS:
*vp = UINT_TO_JSVAL(event->days);
break;
case EVENT_PROP_MDAYS:
*vp = UINT_TO_JSVAL(event->mdays);
break;
case EVENT_PROP_MONTHS:
*vp = UINT_TO_JSVAL(event->months);
break;
case EVENT_PROP_LAST_RUN:
*vp = UINT_TO_JSVAL(event->last);
break;
case EVENT_PROP_NEXT_RUN:
*vp = UINT_TO_JSVAL((uint32)getnexteventtime(event));
break;
case EVENT_PROP_ERRLEVEL:
*vp = UINT_TO_JSVAL(event->errlevel);
break;
case EVENT_PROP_MISC:
*vp = UINT_TO_JSVAL(event->misc);
break;
}
if(p != NULL) { /* string property */
if((js_str = JS_NewStringCopyZ(cx, p)) == NULL)
return JS_FALSE;
*vp = STRING_TO_JSVAL(js_str);
}
return JS_TRUE;
}
static JSClass js_event_class = {
"Event" /* name */
,JSCLASS_HAS_PRIVATE /* flags */
,JS_PropertyStub /* addProperty */
,JS_PropertyStub /* delProperty */
,js_event_get /* getProperty */
,JS_StrictPropertyStub /* setProperty */
,JS_EnumerateStub /* enumerate */
,JS_ResolveStub /* resolve */
,JS_ConvertStub /* convert */
,JS_FinalizeStub /* finalize */
};
struct js_xtrn_area_priv {
scfg_t *cfg;
......@@ -407,55 +500,16 @@ JSBool DLLCALL js_xtrn_area_resolve(JSContext* cx, JSObject* areaobj, jsid id)
for(l=0;l<p->cfg->total_events;l++) {
if((eventobj=JS_NewObject(cx, NULL, NULL, NULL))==NULL)
if((eventobj=JS_NewObject(cx, &js_event_class, NULL, NULL))==NULL)
return JS_FALSE;
if(!JS_DefineProperty(cx, event_array, p->cfg->event[l]->code, OBJECT_TO_JSVAL(eventobj)
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE))
return JS_FALSE;
JS_SetPrivate(cx, eventobj, p->cfg->event[l]);
if((js_str=JS_NewStringCopyZ(cx, p->cfg->event[l]->cmd))==NULL)
return JS_FALSE;
if(!JS_DefineProperty(cx, eventobj, "cmd", STRING_TO_JSVAL(js_str)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))
return JS_FALSE;
if((js_str=JS_NewStringCopyZ(cx, p->cfg->event[l]->dir))==NULL)
return JS_FALSE;
if(!JS_DefineProperty(cx, eventobj, "startup_dir", STRING_TO_JSVAL(js_str)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))
return JS_FALSE;
if(!JS_DefineProperty(cx, eventobj, "node_num", INT_TO_JSVAL(p->cfg->event[l]->node)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))
if(!js_DefineSyncProperties(cx, eventobj, js_event_properties))
return JS_FALSE;
if(!JS_DefineProperty(cx, eventobj, "time", INT_TO_JSVAL(p->cfg->event[l]->time)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))
return JS_FALSE;
if(!JS_DefineProperty(cx, eventobj, "freq", INT_TO_JSVAL(p->cfg->event[l]->freq)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))
return JS_FALSE;
if(!JS_DefineProperty(cx, eventobj, "days", INT_TO_JSVAL(p->cfg->event[l]->days)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))
return JS_FALSE;
if(!JS_DefineProperty(cx, eventobj, "mdays", INT_TO_JSVAL(p->cfg->event[l]->mdays)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))
return JS_FALSE;
if(!JS_DefineProperty(cx, eventobj, "months", INT_TO_JSVAL(p->cfg->event[l]->months)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))
return JS_FALSE;
if(!JS_DefineProperty(cx, eventobj, "last_run", INT_TO_JSVAL(p->cfg->event[l]->last)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))
return JS_FALSE;
if(!JS_DefineProperty(cx, eventobj, "settings", INT_TO_JSVAL(p->cfg->event[l]->misc)
,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY))
if(!JS_DefineProperty(cx, event_array, p->cfg->event[l]->code, OBJECT_TO_JSVAL(eventobj)
,NULL,NULL,JSPROP_READONLY|JSPROP_ENUMERATE))
return JS_FALSE;
#ifdef BUILD_JSDOCS
......
......@@ -1179,6 +1179,7 @@ extern "C" {
/* data.cpp */
DLLEXPORT time_t DLLCALL getnextevent(scfg_t* cfg, event_t* event);
DLLEXPORT time_t DLLCALL getnexteventtime(event_t* event);
/* sockopt.c */
DLLEXPORT int DLLCALL set_socket_options(scfg_t* cfg, SOCKET sock, const char* section
......
/* Synchronet configuration structure (scfg_t) definition */
// vi: tabstop=4
/* $Id: scfgdefs.h,v 1.62 2020/08/08 20:17:03 rswindell Exp $ */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
......@@ -16,21 +13,9 @@
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* Anonymous FTP access to the most recent released source is available at *
* ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
* *
* Anonymous CVS access to the development source and modification history *
* is available at cvs.synchro.net:/cvsroot/sbbs, example: *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login *
* (just hit return, no password is necessary) *
* cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* You are encouraged to submit any modifications (preferably in Unix diff *
* format) via e-mail to mods@synchro.net *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
......@@ -305,9 +290,9 @@ typedef struct { /* External Editors */
typedef struct { /* Generic Timed Event */
char code[LEN_CODE+1], /* Internal code */
days, /* Week days to run event */
dir[LEN_DIR+1], /* Start-up directory */
cmd[LEN_CMD+1]; /* Command line */
uint8_t days; /* Week days to run event */
uint16_t node, /* Node to execute event */
time, /* Time to run event */
freq; /* Frequency to run event */
......