/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1990-2011 AT&T Intellectual Property *
* and is licensed under the *
* Eclipse Public License, Version 1.0 *
* by AT&T Intellectual Property *
* *
* A copy of the License is available at *
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
* *
* Information and Software Systems Research *
* AT&T Research *
* Florham Park NJ *
* *
* Glenn Fowler <gsf@research.att.com> *
* *
***********************************************************************/
#pragma prototyped
/*
* Glenn Fowler
* AT&T Research
*
* shared event daemon
*/
#define EVENT_MINOR 0
static const char usage[] =
"[-?\n@(#)$Id: event (AT&T Research) 2007-06-05 $\n]"
"[+NAME?event - shared event client and server]"
"[+DESCRIPTION?\bevent\b is a shared event client and server. Events are "
"stored in a persistent database named by the \aname\a operand. Each "
"event has a name, an expiration, and a binary status \braised\b or "
"\bnot-raised\b. A non-existent event is \bnot-raised\b. Events may be "
"raised, deleted, cleared, tested and waited for. If no \brequest\b "
"operands are specified then requests are prompted for, with an "
"\bEVENT>\b prompt, and read from the standard input. Multiple command "
"line requests must be separated by \b:\b. In the following events "
"operands are matched by \bksh\b(1) patterns. The client requests are:]"
"{"
"[+all \aconnection\a?Raise all pending events for the "
"\aconnection\a. Mainly for debugging.]"
"[+clear \aevent\a ...?Mark \aevent\a not-raised but do not "
"delete from the database. This allows the events to be matched "
"by patterns.]"
"[+delete \aevent\a ...?Delete \aevent\a.]"
"[+exit?Close the client connection.]"
"[+hold [ \aevent\a ...]]?If \aevent\a operands are specified "
"then clients are not notified about the those events until they "
"are explicitly released by \brelease\b \aevent\a ... If no "
"events are specified then all current and future events will be "
"unconditionally held until a \brelease\b with no event "
"operands.]"
"[+info?List the server status pending events by client "
"connection. The list is terminated by a \bdone\b message.]"
"[+list [ \apattern\a ]]?Start an event dictionary scan and list "
"the first event. If \apattern\a is specified then only events "
"matching \apattern\a are listed.]"
"[+next?List the next event in the \blist\b event scan. The list "
"is terminated by a \bdone\b message.]"
"[+quit?Equivalent to exit.]"
"[+raise \aevent\a ...?Raise \aevent\a ...]"
"[+release [ \aevent\a ...]]?If \aevent\a operands are specified "
"then they are released from a previous \bhold\b \aevent\a ... "
"If no \aevent\a operands are specified then any previous "
"unconditional \bhold\b is turned off.]"
"[+set \aoption\a ...?]"
"[+stop?Terminate the server. Persistent data is preserved.]"
"[+test \aevent\a?Determine the \aevent\a status.]"
"[+wait \aevent\a?Wait for \aevent\a status to be \braised\b.]"
"}"
"[+?The \b--cs\b, \b--expire\b, \b--initialize\b, and \b--log\b options "
"apply to the initial service command, and the \b--expire\b, \b--log\b, "
"\b--newer\b, \b--older\b, and \b--quiet\b options apply to client "
"requests.]"
"[c:cs|connect-stream?Use \aconnect-stream\a instead of the default.]"
"[e:expire?Set the current event expiration to the \bdate\b(1) or "
"\bcron\b(1) expression \adate-expression\a.]:[date-expression]"
"[i:initialize?Initialize the service if it is not already running.]"
"[l!:log?Log server activity to \astate-name\a.log, where \astate-name\a "
"is the state path name sans suffix.]"
"[n:newer?Match events newer than \adate\a. If \b--older\b is also "
"specified then only event times > newer and < older match.]:[date]"
"[o:older?Match events older than \adate\a. If \b--newer\b is also "
"specified then only event times > newer and < older match.]:[date]"
"[q:quiet?Suppress request confirmation messages.]"
"\n"
"\nname [ request [ options ] [ arg ... ] ] [ : request ... ]\n"
"\n"
"[+CAVEATS?Expirations, logging and the \bset\b request are not "
"implemented yet.]"
"[+SEE ALSO?\bcoshell\b(1), \bcs\b(1), \bnmake\b(1), \bdbm\b(3), "
"\bndbm\b(3), \bgdbm\b(3)]"
;
#include <ast.h>
#include <cdt.h>
#include <css.h>
#include <ctype.h>
#include <debug.h>
#include <error.h>
#include <namval.h>
#include <ls.h>
#include <regex.h>
#include <stdarg.h>
#include <swap.h>
#include <tok.h>
#include <tm.h>
#include <ast_ndbm.h>
#if !_use_ndbm
int
{
return 1;
}
#else
{
} Data_t;
{
} Event_t;
{
} Waiting_t;
{
} Connection_t;
{
} Request_t;
{
} State_t;
{
"e*xit", REQ_exit, 0, 0,
"i*nfo", REQ_info, 0, 0,
"n*ext", REQ_next, 0, 0,
"q*uit", REQ_exit, 0, 0,
"s*et", REQ_set, 0, 0,
"stop", REQ_stop, 0, 0,
};
/*
* generate a user response and log message
*/
static void
{
char* s;
if (format)
if (type)
{
{
if (type == 'W')
else if (type == 'E')
}
}
}
/*
* accept a new connection
*/
static int
{
return -1;
}
/*
* notify connections waiting on ep
*/
static int
{
char* s;
size_t n;
for (cp = (Connection_t*)dtfirst(state->connections); cp; cp = (Connection_t*)dtnext(state->connections, cp))
{
{
}
if (n)
break;
}
return 0;
}
/*
* post pending event name for connection
*/
static int
{
{
return -1;
}
{
return 0;
}
{
return -1;
}
else
{
}
{
return -1;
}
return 0;
}
/*
*/
static int
{
int n;
log(state, con, 'I', "info server='%s' version=%d.%d host=%s pid=%d uid=%d gid=%d", fmtident(usage), EVENT_MAJOR, EVENT_MINOR, csname(css->state, 0), getpid(), geteuid(), getegid());
for (cp = (Connection_t*)dtfirst(state->connections); cp; cp = (Connection_t*)dtnext(state->connections, cp))
{
}
return 0;
}
/*
* apply request r to one key
*/
static int
{
Event_t* e;
int n;
switch (index)
{
case REQ_clear:
if (!(n = dbm_store(state->dbm, key, val, DBM_INSERT)) || n > 0 && !dbm_store(state->dbm, key, val, DBM_REPLACE))
else
{
}
break;
case REQ_delete:
{
return 1;
}
else
{
}
break;
case REQ_hold:
if (!(n = dbm_store(state->dbm, key, val, DBM_INSERT)) || n > 0 && !dbm_store(state->dbm, key, val, DBM_REPLACE))
else
{
}
break;
case REQ_raise:
if (!(n = dbm_store(state->dbm, key, val, DBM_INSERT)) || n > 0 && !dbm_store(state->dbm, key, val, DBM_REPLACE))
{
}
else
{
}
break;
case REQ_release:
{
{
if (!(n = dbm_store(state->dbm, key, val, DBM_INSERT)) || n > 0 && !dbm_store(state->dbm, key, val, DBM_REPLACE))
else
{
}
}
else
{
}
}
break;
case REQ_test:
{
else
}
else
break;
case REQ_wait:
return -1;
break;
}
return 0;
}
/*
* apply request r to args a
*/
static int
request(State_t* state, Connection_t* con, int id, int index, char** a, unsigned long older, unsigned long newer)
{
char* s;
int i;
Event_t* e;
while (s = *a++)
{
}
{
if (!EVENT(s))
{
return -1;
}
{
}
else
return -1;
}
else
{
{
{
return -1;
if (i > 0)
goto rescan;
}
}
}
return 0;
}
/*
*/
static unsigned long
{
unsigned long t;
char* e;
{
}
else
{
if (*e)
{
t = 0;
}
}
return t;
}
/*
* service a request
*/
static int
{
char* s;
char* t;
char** a;
char** q;
Cssfd_t* f;
Request_t* r;
Event_t* e;
Waiting_t* w;
Connection_t* x;
int n;
int err;
int id;
unsigned long older;
unsigned long newer;
{
case CS_POLL_CLOSE:
return 0;
case CS_POLL_READ:
return -1;
{
id = -1;
{
while (isalnum(*++s));
if (*s != '=')
break;
if ((s - *q) == 2 && *(s - 1) == 'd' && *(s - 2) == 'i')
}
s = *(a = q);
else
{
err = 0;
for (;;)
{
{
case 'e':
{
if (*t)
{
err = 1;
break;
}
}
continue;
case 'l':
continue;
case 'n':
continue;
case 'o':
continue;
case 'q':
continue;
case '?':
case ':':
err = 1;
break;
}
break;
}
if (!err)
{
{
{
a[0] = "*";
a[1] = 0;
n = 1;
}
else
n = 0;
}
else
n = a[1] ? 2 : 1;
sfprintf(state->usrf, "E %s: at least %d argument%s expected\n", s, r->min, r->min == 1 ? "" : "s");
else
switch (r->index)
{
case REQ_all:
n = (int)strtol(a[0], &t, 0);
if (*t)
{
break;
}
{
break;
}
if (x->waiting)
{
n = x->quiet;
x->quiet = 1;
{
{
*a = 0;
break;
}
}
{
*a = 0;
}
x->quiet = n;
}
break;
case REQ_clear:
case REQ_delete:
case REQ_raise:
case REQ_test:
case REQ_wait:
return -1;
break;
case REQ_exit:
break;
case REQ_info:
break;
case REQ_hold:
if (!*a)
{
}
else
return -1;
break;
case REQ_list:
if (s = *a)
{
{
break;
}
}
{
break;
}
goto list;
case REQ_next:
{
break;
}
for (;;)
{
{
break;
}
list:
{
{
log(state, con, 'I', "event %s %s %d%s%s", con->list.dptr, fmttime("%K", data.time), data.raise, (data.flags & DATA_clear) ? " CLEAR" : "", (data.flags & DATA_hold) ? " HOLD" : "");
break;
}
}
}
break;
case REQ_release:
if (!*a)
{
{
}
}
else
return -1;
break;
case REQ_set:
break;
case REQ_stop:
exit(0);
break;
}
}
}
{
if (id >= 0)
{
}
return -1;
}
}
return 1;
}
return 0;
}
/*
* handle exceptions
*/
static int
{
switch (op)
{
case CSS_CLOSE:
{
}
return 0;
case CSS_INTERRUPT:
return 0;
case CSS_WAKEUP:
return 0;
}
return -1;
}
/*
* free connection
*/
static void
{
}
/*
* free event
*/
static void
{
}
/*
* free connection pending event
*/
static void
{
}
/*
* open and verify event db
*/
static void
{
if (!(state->dbm = dbm_open(state->path, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)))
{
u4 = IDENT_SWAP;
}
else
{
{
}
}
}
/*
*/
int
{
char* s;
char* p = 0;
int server = 0;
/*
* check the options
*/
for (;;)
{
{
case 'c':
continue;
case 'e':
if (*s)
continue;
case 'i':
server = 1;
continue;
case 'l':
continue;
case '?':
continue;
case ':':
continue;
}
break;
}
/*
* get the connect stream path
*/
s++;
else
if (p)
else
/*
* either server or client at this point
*/
if (server)
{
return 1;
{
else
}
return 1;
}
}
#endif