/***********************************************************************
* *
* This software is part of the ast package *
* Copyright (c) 1996-2012 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
*
* at.svc -- the other side of at
*
* the at control dir hierarchy, all files owned by one user
*
* $INSTALLROOT/lib/at
* jobs AT_DIR_MODE
* <host> AT_DIR_MODE
* <job-i> AT_JOB_MODE
*
* job names are encoded with <uid.gid.time> base 64 where
* <time> is the earliest absolute time the job can be run
*/
#include "at.h"
#include <cdt.h>
#include <debug.h>
#include <dirent.h>
#include <proc.h>
#include <pwd.h>
#include <sig.h>
#include <wait.h>
#ifndef SIGCHLD
#endif
#define LOAD 0
#define WAIT 0
typedef struct
{
char* home;
int pending;
int running;
unsigned long admin;
unsigned long uid;
unsigned long total;
} User_t;
typedef struct
{
int allow;
int pending;
int running;
unsigned long total;
} Owner_t;
typedef struct
{
int allow;
int home;
int nproc;
int peruser;
int load;
int nice;
int wait;
int pending;
int running;
int specific;
unsigned long total;
} Queue_t;
typedef struct
{
unsigned long start;
char* period;
char* repeat;
char* shell;
int mail;
unsigned long pid;
unsigned long run;
unsigned long total;
} Job_t;
typedef struct
{
int fd;
} Connection_t;
typedef struct
{
} Table_t;
typedef struct
{
struct
{
} table;
} State_t;
typedef struct
{
} Visit_t;
static const char* export[] =
{
};
static const char* queuedefs[] =
{
};
/*
* return user info given name or uid
*/
static User_t*
{
register int n;
char* home;
if (!(usr = name ? (User_t*)dtmatch(state->table.user.handle, name) : (User_t*)dtmatch(state->table.uid.handle, &uid)))
{
{
}
else
{
if (!name)
home = "/tmp";
}
else
{
{
break;
}
}
}
return usr;
}
/*
* check if usr is permitted on que
* Owner_t allocated and returned if ok
*/
static Owner_t*
{
{
return 0;
{
}
}
return 0;
return own;
}
/*
* initialize owner.allow
*/
static int
{
return 0;
}
/*
* add queue defined in s
*/
static void
{
char* t;
long n;
while (isspace(*s))
s++;
if (*s && *s != '#')
{
if (t = strchr(s, '.'))
*t++ = 0;
{
}
while ((s = t) && *s)
{
n = strtol(s, &t, 10);
if (t == s)
n = 1;
switch (*t++)
{
case 0:
case '#':
t = 0;
break;
case 'h':
break;
case 'j':
break;
case 'l':
break;
case 'n':
break;
case 'u':
break;
case 'w':
break;
case ' ':
case '\t':
s = t;
for (;;)
{
while (isspace(*s))
s++;
if (*(t = s) == '#')
break;
while (*s && !isspace(*s))
s++;
if (n = *s)
*s++ = 0;
if (!*t)
break;
{
}
}
t = 0;
break;
}
}
}
}
/*
* update state from the queue, allow and deny files
*/
static void
{
register char* s;
int permit;
char* file;
char* path;
{
{
}
}
/*
*/
for (que = (Queue_t*)dtfirst(state->table.queue.handle); que && que->specific; que = (Queue_t*)dtnext(state->table.queue.handle, que));
{
permit = 1;
else
{
permit = 0;
}
/*
* reset the queue and associated user access
*/
for (que = (Queue_t*)dtfirst(state->table.queue.handle); que; que = (Queue_t*)dtnext(state->table.queue.handle, que))
{
}
/*
* scan and update the access
*/
if (sp)
{
{
for (que = (Queue_t*)dtfirst(state->table.queue.handle); que; que = (Queue_t*)dtnext(state->table.queue.handle, que))
{
{
}
}
}
}
}
}
/*
* add a client connection
*/
static int
{
}
/*
* insert a job into the run list
*/
static void
{
error(0, "%s %s que %s at %s \"%s\"", job->name, fmtuid(job->owner->user->uid), job->queue->name, fmttime(AT_TIME_FORMAT, job->start), job->label);
}
/*
* remove job from the run list
*/
static void
{
{
}
{
{
}
}
{
{
}
}
}
/*
* drop a job
*/
static void
{
error(0, "%s %s que %s drop \"%s\"", job->name, fmtuid(job->owner->user->uid), job->queue->name, job->label);
}
/*
* pass job to atx for impersonation and execution
*/
static unsigned long
{
unsigned long pid;
argv[3] = 0;
ops[1] = 0;
return 0;
return pid;
}
/*
* reap the state for a job that has completed
*/
static void
{
char* e;
time_t t;
error(0, "%s %s %lu exit %d \"%s\"", job->name, fmtuid(job->owner->user->uid), job->pid, status, job->label);
{
}
else
}
/*
* execute ready jobs
*/
static int
{
unsigned long x = SMAX;
for (job = (Job_t*)dtfirst(state->table.job.handle); job; job = (Job_t*)dtnext(state->table.job.handle, job))
{
message((-2, "schedule job=%s start=%lu time=%lu queue=%s load=%d.%02d/%d.%02d%s", job->name, job->start, cs.time, job->queue->name, job->queue->load / 100, job->queue->load % 100, st.load / 100, st.load % 100, job->run ? " RUNNING" : job->start <= cs.time ? " READY" : ""));
{
{
{
{
}
}
}
else
{
break;
}
}
}
if (x == SMAX)
{
}
return 0;
}
static int
{
int status;
switch (op)
{
case CSS_INTERRUPT:
for (;;)
{
{
case -1:
continue;
break;
case 0:
break;
default:
continue;
}
break;
}
return 1;
case CSS_WAKEUP:
return 1;
}
return -1;
}
/*
* list owner names in handle
*/
static int
{
return 0;
}
/*
* list queue status
*/
static int
{
char* s;
if (!s[2])
*s = 0;
error(ERROR_OUTPUT|0, con->fd, "%-3s %5lu %3d %3d %3d %3d %3d %2d.%02d %5.5s%s", que->name, que->total, que->pending, que->running, que->nproc, que->peruser, que->nice, que->load / 100, que->load % 100, fmtelapsed(que->wait, 1), s);
return 0;
}
/*
* output name='value'
*/
static void
{
register int c;
while (c = *v++)
{
if (c == '\'')
}
}
/*
* execute the at command in s
*/
static int
{
char* t;
char* u;
char* b;
char* h;
int c;
int admin;
int mail;
int skip;
unsigned long m;
unsigned long w;
long x;
b = s;
admin = 0;
if (*++s == AT_ADMIN)
{
s++;
goto denied;
admin = 1;
}
if (*s == AT_QUEUE)
{
t = ++s;
if (!(s = strchr(s, ' ')))
{
return -1;
}
*s = 0;
{
return -1;
}
*s++ = ' ';
goto noqueue;
}
else
que = 0;
s++;
switch (c = *s++)
{
case AT_ACCESS:
goto noqueue;
break;
case AT_DEBUG:
goto denied;
break;
case AT_INFO:
error(ERROR_OUTPUT|0, con->fd, "at service daemon pid %ld user %s", state->con[0].id.pid, fmtuid(state->admin[0]));
if (que)
else
break;
case AT_JOB:
if (!que)
goto noqueue;
{
return -1;
}
{
return -1;
}
for (s = t; *s && *s != '\n'; s++);
h = s + 1;
{
return -1;
}
if (data)
else
sfsprintf(job->name, sizeof(job->name), "%..36lu.%..36lu.%..36lu", con->id.uid, con->id.gid, state->sequence++);
while (*s)
if (*s++ == ' ')
{
*(s - 1) = 0;
break;
}
while (c = *s++)
{
switch (c)
{
case AT_LABEL:
*t++ = *s;
if (*s)
s++;
continue;
case AT_TIME:
s = t;
if (*s == ' ')
s++;
if (*s)
{
if (*s == '+')
{
while (*++s && !isspace(*s));
while (isspace(*s))
s++;
}
}
break;
default:
break;
}
break;
}
{
m = 0;
t = b + skip;
if (*t == ':')
{
while (isspace(*++t));
if (u = strchr(t, ';'))
{
while (u > t && isspace(*(u - 1)))
u--;
m = u - t;
}
}
if (m)
{
}
else
{
for (t = b + skip; *t; t++)
if (isalnum(*t))
{
u = t;
while (isalnum(*++t));
c = t - u;
if (c == 3 && strneq(u, "for", 3) || c == 2 && strneq(u, "if", 2) || c == 5 && strneq(u, "while", 5))
continue;
break;
}
m = 0;
x = -1;
while (c = *t++)
{
if (isalnum(c))
{
if (x)
{
if (x > 0)
{
break;
}
x = 0;
}
break;
}
else if (!x)
x = 1;
}
}
}
{
goto noexec;
sfprintf(sp, "#%c%s %c0 %s %c%s %c%lu", AT_QUEUE, job->queue->name, AT_JOB, job->shell, AT_LABEL, t, AT_TIME, job->start);
c = h - b;
b += c;
n -= c;
skip -= c;
x = 0;
for (c = 0; c < elementsof(export); c++)
{
x = 1;
}
{
b += skip;
n -= skip;
x = 1;
}
b[n - 1] = '\n';
goto noexec;
{
sp = 0;
goto noexec;
}
error(ERROR_OUTPUT|0, con->fd, "job %s at %s%s%s", job->name, fmttime("%a %b %e %T %Y", job->start), job->repeat ? " repeat " : "", job->period);
}
break;
case AT_LIST:
case AT_REMOVE:
case AT_STATUS:
if (*s++ != ' ')
s = 0;
m = 0;
{
if ((!que || que == job->queue) && (s && strmatch(job->name, s) || !s && (admin || con->id.uid == job->id.uid)) && job->owner->allow)
switch (c)
{
case AT_LIST:
break;
case AT_STATUS:
if (!m++)
error(ERROR_OUTPUT|0, con->fd, "%-21s %-*s%7d %-1s %.-8s %s %s", job->name, sizeof(job->label), job->label, job->pid, job->queue->name, job->owner->user->name, fmttime(AT_TIME_FORMAT, job->start), job->period);
break;
case AT_REMOVE:
{
m++;
}
else
break;
}
}
if (s && !m)
{
return -1;
}
break;
case AT_LOG:
pathcanon(s, 0, 0);
break;
case AT_QUIT:
goto denied;
exit(0);
break;
case AT_UPDATE:
goto denied;
if (*s)
break;
case AT_USER:
for (usr = (User_t*)dtfirst(state->table.user.handle); usr; usr = (User_t*)dtnext(state->table.user.handle, usr))
error(ERROR_OUTPUT|0, con->fd, "%-9.9s %5lu %5lu %3d %3d %s", usr->name, usr->admin, usr->total, usr->pending, usr->running, usr->home);
break;
case AT_VERSION:
break;
default:
return -1;
}
return 0;
return -1;
if (sp)
return -1;
return -1;
}
/*
* order job by <start,name>
*/
static int
{
register long r1;
register int r2;
return 0;
if (!(r1 = *(unsigned long*)a - *(unsigned long*)b))
return r2;
return r1;
}
/*
* commit to the log file
* if its too big then rename to .old and start fresh
*/
static void
commit(void)
{
int rolled;
int fd;
time_t t;
unsigned long now;
static int commiting = 0;
if (commiting++)
{
commiting--;
return;
}
if (!rollover)
{
}
{
rolled = 1;
error(0, "log file rollover");
t = rollover;
}
else
rolled = 0;
if ((fd = open(AT_LOG_FILE, O_CREAT|O_WRONLY|O_APPEND, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0 || fd != 2 && (dup2(fd, 2) != 2 || close(fd)))
if (rolled)
error(0, "log file rollover");
commiting--;
}
/*
* prepend current date-time to buffer on fd==2
* and drop the initial command label if any
*/
static ssize_t
{
register char* s;
register int i;
register ssize_t r;
register ssize_t z;
unsigned long now;
r = 0;
if (rollover)
{
commit();
}
else if (!now)
{
i = strlen(s);
s[i++] = ' ';
r = -1;
else
r += z;
if (*s == ':')
{
n -= s - (char*)buf;
buf = (void*)s;
break;
}
}
r = -1;
else if (r >= 0)
r += z;
return r;
}
/*
* service a request
*/
static int
{
register char* s;
char* t;
unsigned long n;
int c;
return -1;
{
if (c)
return -1;
}
{
return -1;
}
{
{
return -1;
}
}
{
return -1;
}
s[n - 1] = 0;
return -1;
}
/*
* initialize the state
*/
static int
{
char* s;
char* b;
int i;
unsigned long limited;
unsigned long* ap;
i = 20;
*ap++ = 0;
if (!pathpath(AT_JOB_DIR, "", PATH_ABSOLUTE|PATH_EXECUTE, s, state->bufsiz) || lstat(s, &ds) || !AT_DIR_OK(&ds))
error(ERROR_SYSTEM|3, "%s: job directory uid %d != effective uid %d", s, ds.st_uid, state->admin[0]);
return -1;
b = s + strlen(s);
*b++ = '/';
error(ERROR_SYSTEM|3, "%s: invalid job host directory %s [dir.uid=%d host.uid=%d]", s, fmtmode(hs.st_mode, 0), ds.st_uid, hs.st_uid);
error(AT_STRICT, "%s: directory owner %s does not match daemon %s", s, fmtuid(hs.st_uid), fmtuid(state->admin[0]));
pathcanon(s, 0, 0);
#if 0
error(3, "%s: invalid [ mode=%04o uid=%d euid=%d t1=%04o t2=%04o==%04o ]", s, xs.st_mode, xs.st_uid, geteuid(), S_ISREG(xs.st_mode), xs.st_mode&(S_IXUSR|S_IXGRP|S_IWOTH|S_IXOTH), (S_IXUSR|S_IXGRP|S_IXOTH));
#endif
for (i = 0; i < elementsof(queuedefs); i++)
commit();
if (limited)
/*
* update the queue definitions
*/
/*
* resubmit old jobs
*/
{
{
error(0, "cannot stat old job %s", s);
remove(s);
}
{
{
rmdir(s);
else
remove(s);
}
}
else if (sfsscanf(s, "%..36lu.%..36lu.%..36lu", &state->con[0].id.uid, &state->con[0].id.gid, &cs.time) != 3)
{
error(0, "invalid old job %s name rejected", s);
remove(s);
}
{
error(0, "invalid old job %s mode %s rejected [dir.uid=%d job.uid=%d]", s, fmtmode(js.st_mode, 0), hs.st_uid, js.st_uid);
remove(s);
}
{
error(0, "cannot read old job %s", s);
remove(s);
}
else
{
}
}
CSTIME();
return 0;
}
int
{
char* path;
int status;
/*
* monitor the daemon and restart if it dies
*/
for (;;)
{
{
return 1;
}
if (!status)
break;
sleep(60);
}
return 0;
}