Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/at/at.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1996-2011 AT&T Intellectual Property *
5
* and is licensed under the *
6
* Eclipse Public License, Version 1.0 *
7
* by AT&T Intellectual Property *
8
* *
9
* A copy of the License is available at *
10
* http://www.eclipse.org/org/documents/epl-v10.html *
11
* (with md5 checksum b35adb5213ca9657e911e9befb180842) *
12
* *
13
* Information and Software Systems Research *
14
* AT&T Research *
15
* Florham Park NJ *
16
* *
17
* Glenn Fowler <[email protected]> *
18
* *
19
***********************************************************************/
20
#pragma prototyped
21
/*
22
* Glenn Fowler
23
* AT&T Research
24
*
25
* at -- bundle request and send to AT_SERVICE
26
*/
27
28
static const char usage[] =
29
"[-?\n@(#)$Id: at (AT&T Research) 2000-05-09 $\n]"
30
USAGE_LICENSE
31
"[+NAME?\f?\f - run commands at specified time(s)]"
32
"[+DESCRIPTION?\b\f?\f\b is the command interface to the \bat\b daemon."
33
" It submits commands to be executed at a future time, lists"
34
" queue status, and controls the daemon.]"
35
"[+?Options that refer to specific jobs interpret the operands as job ids,"
36
" otherwise if the \b--time\b option is not specified then the operands"
37
" are interpreted as the start time. If \atime\a is not specified then"
38
" the command is scheduled to be executed immediately, subject to the"
39
" queue constraints.]"
40
"[+?If \b--time\b is specified then the first non-option argument is the"
41
" command to be executed, otherwise the command to be executed is read"
42
" from the standard input. The command job id is written to the standard"
43
" output after the command has been successfully submitted to the"
44
" daemon.]"
45
46
"[a:access?Check queue access and exit.]"
47
"[f:file?\afile\a is a script to be run at the specified time.]:[file]"
48
"[h:label|heading?Set the job label to \astring\a.]:[string]"
49
"[i:info?List queue information and exit.]"
50
"[l:list?List queue jobs and exit.]"
51
"[m:mail?Send mail when the command completes.]"
52
"[n!:exec?Execute the command. \b--noexec\b shows what would be done but"
53
" does not execute.]"
54
"[p:status?List detailed queue status.]"
55
"[q:queue?Send request to \aqueue\a. Standard queues are:]:[queue]{"
56
" [+a?The \bat\b(1) queue.]"
57
" [+b?The \bbatch\b(1) queue.]"
58
" [+c?The \bcron\b(1) queue.]"
59
"}"
60
"[r:remove?Remove command named by the \ajob\a ... operands from the queue.]"
61
"[s:service?Connect to the \bcs\b(1) service \apath\a.]:"
62
" [path:=/dev/tcp/local/at]"
63
"[t:time|date?Schedule the command to be run at \atime\a. Most common"
64
" date formats are accepted. The keyword \bevery\b can be combined"
65
" with date parts to specify repeat executions, e.g.,"
66
" \bevery Monday\b.]:[time:=now]"
67
"[u:user?List the per user queue information. Requires sufficient privilege"
68
" to view the status of other users.]"
69
"[A:admin?Enable administrative actions. Requires sufficient privilege.]"
70
"[D:debug?Enable \bat\b daemon debug tracing to \afile\a at debug level"
71
" \alevel\a. Higher levels produce more output. Requires sufficient"
72
" privilege.]:[[level]][file]]]"
73
"[L:log?Write the log file path name on the standard output and exit. The log"
74
" file is renamed with a \b.old\b suffix at the beginning of each month"
75
" and a new log file started.]"
76
"[Q:quit?Terminate the \bat\b daemon. Requires sufficient privilege.]"
77
"[U:update?Causes the \bat\b daemon to do a schedule update and re-read"
78
" the queue definition file if it has changed from the last time"
79
" it was read. If \aqueuedef\a is specified then it is interpreted as"
80
" a queue definition file line. See \bQUEUE DEFINITIONS\b below."
81
" Requires sufficient privilege.]:?[queuedef]"
82
"[+QUEUE DEFINITIONS?(\aNOTE\a: the \bnroff\b(1) style syntax is taken from"
83
" \bX/Open\b). The queue definition file defines queue attributes,"
84
" one queue per line. Lines starting with \b#\b are comments. The format"
85
" of a definition line is: \aname\a.[\anumber\a\battribute\b]]..., where"
86
" \aname\a is a single letter queue name and \anumber\a applies to the"
87
" following single character \battribute\b. If \anumber\a is omitted"
88
" then it defaults to \b1\b. The default queues are: \ba.4j1n2u\b,"
89
" \bb.2j2n90w2u\b, \bc.h8j2u60w\b. Per-queue user access may be"
90
" specified by appending a space separated user names after the queue"
91
" attributes. If the first list element is \b+\b then the list"
92
" specifies users allowed to use the queue; othewise it specifies users"
93
" denied access to the queue. If no user list is specified then queue"
94
" access is controlled by the global files described in"
95
" \bQUEUE ACCESS\b below. The attributes are:]{"
96
" [+h?The job environment is initialized to contain at least the"
97
" \bHOME\b, \bLOGNAME\b, \bUSER\b, \bPATH\b and"
98
" \bSHELL\b of the submitting user. The jobs are also"
99
" run in the user \bHOME\b directory.]"
100
" [+j?The total number of running jobs for all queues is limited"
101
" to \anumber\a.]"
102
" [+l?No new jobs will be run until the load average is smaller"
103
" than \anumber\a. This only works on systems where the"
104
" load average is easily determined.]"
105
" [+n?The default \bnice\b(1) priority is set to \anumber\a.]"
106
" [+u?The per-user running job limit is set to \anumber\a.]"
107
" [+w?At least \anumber\a seconds will elapse before the"
108
" next job from the queue is run.]"
109
" }"
110
"[+QUEUE ACCESS?The user \broot\b may submit jobs to all queues. If a queue"
111
" definition does not specify a user access list then the queue access"
112
" is controlled by the default access files in this order:]{"
113
" [+(1)?If the directory \b/usr/lib/cron\b does not exist then"
114
" job access is granted to all users.]"
115
" [+(2)?If the file \b/usr/lib/cron/at.allow\b exists then"
116
" access is granted only to user names listed in this"
117
" file, one name per line.]"
118
" [+(3)?If the file \b/usr/lib/cron/at.deny\b exists then"
119
" access is denied to user names listed in this file,"
120
" one name per line.]"
121
" [+(4)?Otherwise access is denied to all users but \broot\b.]"
122
" }"
123
124
"\n"
125
"\n[ job ... | time ... ]\n"
126
"\n"
127
128
"[+FILES]{"
129
" [+/usr/lib/at/queuedefs?The default queue definition file.]"
130
" [+/usr/lib/cron/at.(allow|deny)?The default queue access files.]"
131
"}"
132
"[+SEE ALSO?\bbatch\b(1), \bcrontab\b(1), \bnice\b(1), \bsh\b(1)]"
133
;
134
135
#include "at.h"
136
137
#include <fs3d.h>
138
139
#define ACCESS (1<<0)
140
#define ADMIN (1<<1)
141
#define EXEC (1<<2)
142
#define INFO (1<<3)
143
#define JOB (1<<4)
144
#define LIST (1<<5)
145
#define LOG (1<<6)
146
#define MAIL (1<<7)
147
#define QUIT (1<<8)
148
#define REMOVE (1<<9)
149
#define STATUS (1<<10)
150
#define TRACE (1<<11)
151
#define UPDATE (1<<12)
152
#define USER (1<<13)
153
#define VERSION (1<<14)
154
155
int
156
main(int argc, char** argv)
157
{
158
register int c;
159
register char* s;
160
register Sfio_t* sp;
161
register Sfio_t* tp;
162
char* e;
163
char* t;
164
int n;
165
int skip;
166
time_t now;
167
char buf[PATH_MAX];
168
169
int flags = 0;
170
int interval = 1;
171
int op = EXEC;
172
char* file = 0;
173
char* label = 0;
174
char* queue = 0;
175
char* service = AT_SERVICE;
176
char* date = 0;
177
char* every = 0;
178
char* trace = 0;
179
char* update = 0;
180
time_t start = 0;
181
182
static char que[2];
183
184
if (!(s = strrchr(*argv, '/')))
185
s = *argv;
186
else if (t = strchr(++s, '_'))
187
s = t + 1;
188
if (*s == 'b')
189
{
190
error_info.id = "batch";
191
*(queue = que) = *s;
192
op |= MAIL;
193
date = "now";
194
}
195
else
196
error_info.id = "at";
197
for (;;)
198
{
199
switch (optget(argv, usage))
200
{
201
case 'a':
202
op |= ACCESS;
203
continue;
204
case 'f':
205
file = opt_info.arg;
206
continue;
207
case 'h':
208
label = opt_info.arg;
209
continue;
210
case 'i':
211
op |= INFO;
212
continue;
213
case 'l':
214
op |= LIST;
215
continue;
216
case 'm':
217
op |= MAIL;
218
continue;
219
case 'n':
220
op &= ~EXEC;
221
continue;
222
case 'p':
223
op |= STATUS;
224
continue;
225
case 'q':
226
queue = opt_info.arg;
227
continue;
228
case 'r':
229
op |= REMOVE;
230
continue;
231
case 's':
232
service = opt_info.arg;
233
continue;
234
case 't':
235
date = opt_info.arg;
236
continue;
237
case 'u':
238
op |= USER;
239
continue;
240
case 'v':
241
op |= VERSION;
242
continue;
243
case 'A':
244
op |= ADMIN;
245
continue;
246
case 'D':
247
op |= TRACE;
248
trace = opt_info.arg;
249
continue;
250
case 'L':
251
op |= LOG;
252
continue;
253
case 'Q':
254
op |= QUIT;
255
continue;
256
case 'U':
257
op |= UPDATE;
258
update = opt_info.arg;
259
continue;
260
case ':':
261
error(2, "%s", opt_info.arg);
262
continue;
263
case '?':
264
error(ERROR_USAGE|4, "%s", opt_info.arg);
265
continue;
266
}
267
break;
268
}
269
argv += opt_info.index;
270
if (error_info.errors)
271
error(ERROR_USAGE|4, "%s", optusage(NiL));
272
if (!(sp = sfstropen()))
273
error(ERROR_SYSTEM|3, "out of space [message]");
274
sfprintf(sp, "#00000\n#");
275
if (op & ADMIN)
276
sfprintf(sp, "%c", AT_ADMIN);
277
if (queue)
278
sfprintf(sp, "%c%s ", AT_QUEUE, queue);
279
switch (op & (ACCESS|INFO|LIST|LOG|QUIT|REMOVE|STATUS|TRACE|UPDATE|USER|VERSION))
280
{
281
case ACCESS:
282
sfputc(sp, AT_ACCESS);
283
break;
284
case INFO:
285
sfputc(sp, AT_INFO);
286
break;
287
case LIST:
288
sfputc(sp, AT_LIST);
289
break;
290
case LOG:
291
sfputc(sp, AT_LOG);
292
break;
293
case QUIT:
294
sfputc(sp, AT_QUIT);
295
flags |= CS_OPEN_TEST;
296
break;
297
case REMOVE:
298
sfputc(sp, AT_REMOVE);
299
break;
300
case STATUS:
301
sfputc(sp, AT_STATUS);
302
break;
303
case TRACE:
304
sfprintf(sp, "%c%s", AT_DEBUG, trace);
305
break;
306
case UPDATE:
307
sfprintf(sp, "%c%s", AT_UPDATE, update ? update : "");
308
break;
309
case USER:
310
sfputc(sp, AT_USER);
311
break;
312
case VERSION:
313
sfputc(sp, AT_VERSION);
314
break;
315
default:
316
if (op & ~(EXEC|MAIL))
317
error(3, "only one of -[aglrsuvLQU] may be specified");
318
op |= JOB;
319
if (file && !sfopen(sfstdin, file, "r"))
320
error(ERROR_SYSTEM|3, "%s: cannot read", file);
321
if (!fs3d(FS3D_TEST) || !(s = pathpath("3d", NiL, PATH_ABSOLUTE|PATH_EXECUTE, buf, sizeof(buf))))
322
s = pathshell();
323
if (op & MAIL)
324
sfputc(sp, AT_MAIL);
325
skip = sfstrtell(sp) + 1;
326
sfprintf(sp, "%c00000 %s ", AT_JOB, s);
327
if (date || *argv)
328
{
329
if (!(tp = sfstropen()))
330
error(ERROR_SYSTEM|3, "out of space [time]");
331
sfputr(tp, "+0 ", -1);
332
if (date)
333
sfputr(tp, date, -1);
334
else
335
for (s = *argv;;)
336
{
337
for (n = ' '; c = *s++; n = c)
338
if (!isspace(c) || n != (c = ' '))
339
sfputc(tp, c);
340
if (!(s = *++argv))
341
break;
342
if (n != ' ')
343
sfputc(tp, ' ');
344
}
345
if (!(s = sfstruse(tp)))
346
error(ERROR_SYSTEM|3, "out of space");
347
s += 3;
348
if (strneq(s, "cron ", n = 5) || strneq(s, "each ", n = 5) || strneq(s, "every ", n = 6) || strneq(s, "repeat ", n = 7))
349
{
350
every = s + n;
351
t = s;
352
*t++ = '+';
353
*t++ = '0';
354
while (*t != ' ')
355
*t++ = ' ';
356
if (*++t == '+' && isdigit(*(t + 1)))
357
{
358
interval = strtol(t, NiL, 0);
359
while (*t && *t != ' ')
360
*t++ = ' ';
361
}
362
else if (isdigit(*t))
363
{
364
interval = strtol(t, &e, 0);
365
if (!isalpha(*e))
366
interval = 1;
367
else
368
while (*t && *t != ' ')
369
*t++ = ' ';
370
}
371
while (*every == ' ')
372
every++;
373
}
374
now = time(NiL);
375
start = tmdate(s, &t, &now);
376
if (!every && (unsigned long)start <= (unsigned long)now)
377
start = tmdate(s - 3, &t, &now);
378
if (*t)
379
error(3, "%s: invalid time specification", s);
380
if (s = label)
381
{
382
sfputc(sp, AT_LABEL);
383
while ((c = *s++) && !isspace(c))
384
if (isalnum(c))
385
sfputc(sp, c);
386
sfputc(sp, ' ');
387
}
388
sfprintf(sp, "%c%lu", AT_TIME, start);
389
if (s = every)
390
sfprintf(sp, " +%d %s", interval, s);
391
}
392
break;
393
}
394
if (op & JOB)
395
{
396
if (!(s = coinit(CO_EXPORT|CO_SERVER)))
397
error(ERROR_SYSTEM|3, "cannot generate initialization script");
398
sfprintf(sp, "\n%s\n", s);
399
s = sfstrbase(sp);
400
sfsprintf(s + skip, 6, "%05lu ", sfstrtell(sp) - 7);
401
s[skip + 5] = ' ';
402
if (s = *argv++)
403
{
404
sfprintf(sp, "%s", s);
405
while (s = *argv++)
406
sfprintf(sp, " %s", s);
407
}
408
else
409
sfmove(sfstdin, sp, SF_UNBOUND, -1);
410
sfputc(sp, '\n');
411
}
412
else
413
{
414
if (s = *argv++)
415
{
416
if (streq(s, "-") && !*argv)
417
s = "*";
418
sfprintf(sp, " %s", s);
419
while (s = *argv++)
420
sfprintf(sp, "|%s", s);
421
}
422
sfputc(sp, '\n');
423
}
424
n = sfstrtell(sp);
425
if (!(s = sfstruse(sp)))
426
error(ERROR_SYSTEM|3, "out of space");
427
sfsprintf(s, 7, "#%05d\n", n - 7);
428
s[6] = '\n';
429
if (op & EXEC)
430
{
431
if ((op = csopen(&cs, service, 0)) < 0)
432
error(ERROR_SYSTEM|3, "%s: cannot open service", service);
433
if (cswrite(&cs, op, s, n) != n)
434
error(ERROR_SYSTEM|3, "%s: service write error", service);
435
while ((n = read(op, buf, sizeof(buf))) > 0)
436
if (write(1, buf, n) != n)
437
error(ERROR_SYSTEM|3, "write error");
438
if (n < 0)
439
error(ERROR_SYSTEM|3, "%s: service read error", service);
440
}
441
else
442
{
443
if (op & JOB)
444
{
445
sfprintf(sfstdout, "submit job to run on %s", fmttime(NiL, start));
446
if (every)
447
sfprintf(sfstdout, " and repeat every +%d %s", interval, every);
448
sfprintf(sfstdout, "\n");
449
}
450
sfputr(sfstdout, s, -1);
451
}
452
return 0;
453
}
454
455