Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
att
GitHub Repository: att/ast
Path: blob/master/src/cmd/at/atd.c
1808 views
1
/***********************************************************************
2
* *
3
* This software is part of the ast package *
4
* Copyright (c) 1996-2012 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.svc -- the other side of at
26
*
27
* the at control dir hierarchy, all files owned by one user
28
*
29
* $INSTALLROOT/lib/at
30
* jobs AT_DIR_MODE
31
* <host> AT_DIR_MODE
32
* <job-i> AT_JOB_MODE
33
*
34
* job names are encoded with <uid.gid.time> base 64 where
35
* <time> is the earliest absolute time the job can be run
36
*/
37
38
static const char id[] = "\n@(#)$Id: at.svc (AT&T Research) 2012-02-29 $\0\n";
39
40
#include "at.h"
41
42
#include <cdt.h>
43
#include <debug.h>
44
#include <dirent.h>
45
#include <proc.h>
46
#include <pwd.h>
47
#include <sig.h>
48
#include <wait.h>
49
50
#ifndef SIGCHLD
51
#define SIGCHLD SIGCLD
52
#endif
53
54
#define HOG (NPROC*10)
55
#define LOAD 0
56
#define MSMAX (LONG_MAX/1000)
57
#define NAME 22
58
#define NICE 2
59
#define NPROC 10
60
#define PERUSER (NPROC/2)
61
#define SMAX ULONG_MAX
62
#define WAIT 0
63
64
typedef struct
65
{
66
Dtlink_t byname;
67
Dtlink_t byuid;
68
char* home;
69
int pending;
70
int running;
71
unsigned long admin;
72
unsigned long uid;
73
unsigned long total;
74
char name[1];
75
} User_t;
76
77
typedef struct
78
{
79
Dtlink_t byuser;
80
User_t* user;
81
int allow;
82
int pending;
83
int running;
84
unsigned long total;
85
} Owner_t;
86
87
typedef struct
88
{
89
Dtlink_t byname;
90
Dt_t* owner;
91
int allow;
92
int home;
93
int nproc;
94
int peruser;
95
int load;
96
int nice;
97
int wait;
98
int pending;
99
int running;
100
int specific;
101
unsigned long total;
102
char name[1];
103
} Queue_t;
104
105
typedef struct
106
{
107
Dtlink_t byname;
108
Dtlink_t bypid;
109
unsigned long start;
110
char name[NAME];
111
Queue_t* queue;
112
Owner_t* owner;
113
char* period;
114
char* repeat;
115
char* shell;
116
int mail;
117
Csid_t id;
118
unsigned long pid;
119
unsigned long run;
120
unsigned long total;
121
char label[11];
122
char data[1];
123
} Job_t;
124
125
typedef struct
126
{
127
int fd;
128
Csid_t id;
129
} Connection_t;
130
131
typedef struct
132
{
133
Dtdisc_t discipline;
134
Dt_t* handle;
135
} Table_t;
136
137
typedef struct
138
{
139
Cssdisc_t disc; /* css discipline */
140
Css_t* css; /* css handle */
141
struct
142
{
143
Table_t job; /* job by <start,name> */
144
Table_t owner; /* user by User_t* */
145
Table_t pid; /* job by pid */
146
Table_t queue; /* queue by name */
147
Table_t uid; /* user by uid */
148
Table_t user; /* user by name */
149
} table;
150
Queue_t* queue; /* default queue */
151
time_t update; /* info file mtime */
152
int nproc; /* total proc limit */
153
int peruser; /* total per user limit */
154
int pending; /* total pending jobs */
155
int running; /* total running jobs */
156
int bufsiz; /* sizeof(*buf) */
157
int init; /* initialization */
158
unsigned long admin[4]; /* admin uids */
159
unsigned long total; /* total number of jobs run */
160
unsigned long sequence; /* job id sequence */
161
char* pwd; /* jobs dir path */
162
char* atx; /* setuid exec full path */
163
char* buf; /* work buffer */
164
Sfio_t* tmp; /* work string */
165
Connection_t con[1]; /* user connections */
166
} State_t;
167
168
typedef struct
169
{
170
Connection_t* con;
171
Queue_t* queue;
172
State_t* state;
173
} Visit_t;
174
175
static const char* export[] =
176
{
177
CO_ENV_HOST, CO_ENV_TYPE
178
};
179
180
static const char* queuedefs[] =
181
{
182
"a.4j1n2u", "b.2j2n90w2u", "c.h8j2u60w"
183
};
184
185
static int schedule(State_t*);
186
187
/*
188
* return user info given name or uid
189
*/
190
191
static User_t*
192
user(register State_t* state, char* name, unsigned long uid)
193
{
194
register User_t* usr;
195
register struct passwd* pwd;
196
register int n;
197
char* home;
198
char buf[16];
199
200
static User_t nobody;
201
202
if (!(usr = name ? (User_t*)dtmatch(state->table.user.handle, name) : (User_t*)dtmatch(state->table.uid.handle, &uid)))
203
{
204
if (pwd = name ? getpwnam(name) : getpwuid(uid))
205
{
206
if (name) uid = pwd->pw_uid;
207
else name = pwd->pw_name;
208
home = pwd->pw_dir;
209
}
210
else
211
{
212
if (!name)
213
sfsprintf(name = buf, sizeof(buf), "%lu", uid);
214
home = "/tmp";
215
}
216
n = strlen(name);
217
if (!(usr = newof(0, User_t, 1, n + strlen(home) + 2)))
218
usr = &nobody;
219
else
220
{
221
usr->uid = uid;
222
strcpy(usr->name, name);
223
usr->home = strcpy(usr->name + n + 1, home);
224
for (n = 0; n < elementsof(state->admin); n++)
225
if (uid == state->admin[n])
226
{
227
usr->admin = 1;
228
break;
229
}
230
}
231
dtinsert(state->table.user.handle, usr);
232
dtinsert(state->table.uid.handle, usr);
233
}
234
return usr;
235
}
236
237
/*
238
* check if usr is permitted on que
239
* Owner_t allocated and returned if ok
240
*/
241
242
static Owner_t*
243
permit(register Queue_t* que, User_t* usr)
244
{
245
register Owner_t* own;
246
247
if (!(own = (Owner_t*)dtmatch(que->owner, &usr)))
248
{
249
if (que->allow > 0)
250
return 0;
251
if (own = newof(0, Owner_t, 1, 0))
252
{
253
own->allow = 1;
254
own->user = usr;
255
dtinsert(que->owner, own);
256
}
257
}
258
else if (!own->allow)
259
return 0;
260
return own;
261
}
262
263
/*
264
* initialize owner.allow
265
*/
266
267
static int
268
allow(Dt_t* dt, void* object, void* handle)
269
{
270
NoP(dt);
271
((Owner_t*)object)->allow = !((Queue_t*)handle)->allow;
272
return 0;
273
}
274
275
/*
276
* add queue defined in s
277
*/
278
279
static void
280
queue(register State_t* state, register char* s)
281
{
282
register Queue_t* que;
283
register Owner_t* own;
284
User_t* usr;
285
char* t;
286
long n;
287
288
while (isspace(*s))
289
s++;
290
if (*s && *s != '#')
291
{
292
if (t = strchr(s, '.'))
293
*t++ = 0;
294
if (!(que = (Queue_t*)dtmatch(state->table.queue.handle, s)))
295
{
296
if (!(que = newof(0, Queue_t, 1, strlen(s) + 1)))
297
error(ERROR_SYSTEM|3, "out of space [queue]");
298
strcpy(que->name, s);
299
que->nproc = NPROC;
300
que->peruser = PERUSER;
301
que->load = LOAD;
302
que->nice = NICE;
303
que->wait = WAIT;
304
que = (Queue_t*)dtinsert(state->table.queue.handle, que);
305
}
306
if (!state->queue)
307
state->queue = que;
308
if (!que->owner && !(que->owner = dtopen(&state->table.owner.discipline, Dtset)))
309
error(ERROR_SYSTEM|3, "out of space [queue %s owner hash]", que->name);
310
que->allow = -1;
311
que->specific = 0;
312
while ((s = t) && *s)
313
{
314
n = strtol(s, &t, 10);
315
if (t == s)
316
n = 1;
317
switch (*t++)
318
{
319
case 0:
320
case '#':
321
t = 0;
322
break;
323
case 'h':
324
que->home = n;
325
break;
326
case 'j':
327
que->nproc = n;
328
break;
329
case 'l':
330
que->load = n;
331
break;
332
case 'n':
333
que->nice = n;
334
break;
335
case 'u':
336
que->peruser = n;
337
break;
338
case 'w':
339
que->wait = n;
340
break;
341
case ' ':
342
case '\t':
343
que->allow = *(t - 2) == '+';
344
que->specific = 1;
345
dtwalk(que->owner, allow, que);
346
s = t;
347
for (;;)
348
{
349
while (isspace(*s))
350
s++;
351
if (*(t = s) == '#')
352
break;
353
while (*s && !isspace(*s))
354
s++;
355
if (n = *s)
356
*s++ = 0;
357
if (!*t)
358
break;
359
usr = user(state, t, 0);
360
if (!(own = (Owner_t*)dtmatch(que->owner, &usr)))
361
{
362
if (!(own = newof(0, Owner_t, 1, 0)))
363
error(ERROR_SYSTEM|3, "out of space [queue owner]");
364
own->user = usr;
365
dtinsert(que->owner, own);
366
}
367
own->allow = que->allow;
368
}
369
t = 0;
370
break;
371
}
372
}
373
}
374
}
375
376
/*
377
* update state from the queue, allow and deny files
378
*/
379
380
static void
381
update(register State_t* state)
382
{
383
register char* s;
384
register Sfio_t* sp;
385
register Queue_t* que;
386
User_t* usr;
387
Owner_t* own;
388
int permit;
389
char* file;
390
char* path;
391
struct stat st;
392
393
sfprintf(state->tmp, "%s/%s/%s", state->pwd, state->pwd, AT_QUEUE_FILE);
394
if (!(path = sfstruse(state->tmp)))
395
error(ERROR_SYSTEM|3, "out of space");
396
pathcanon(path, 0, 0);
397
if (sp = sfopen(NiL, path, "r"))
398
{
399
error(0, "scan %s queue list", path);
400
if (fstat(sffileno(sp), &st) || st.st_mtime != state->update)
401
{
402
state->update = st.st_mtime;
403
if (que = state->queue)
404
state->queue = 0;
405
while (s = sfgetr(sp, '\n', 1))
406
queue(state, s);
407
if (!state->queue && !(state->queue = que))
408
error(ERROR_SYSTEM|3, "%s: no queues", path);
409
}
410
sfclose(sp);
411
}
412
413
/*
414
* apply the global allow/deny files to queues
415
* with no specific allow/deny overrides
416
*/
417
418
for (que = (Queue_t*)dtfirst(state->table.queue.handle); que && que->specific; que = (Queue_t*)dtnext(state->table.queue.handle, que));
419
if (que && !stat(file = AT_CRON_DIR, &st) && S_ISDIR(st.st_mode))
420
{
421
sfprintf(state->tmp, "%s/%s/%s", state->pwd, file, AT_ALLOW_FILE);
422
if (!(path = sfstruse(state->tmp)))
423
error(ERROR_SYSTEM|3, "out of space");
424
pathcanon(path, 0, 0);
425
if (sp = sfopen(NiL, path, "r"))
426
permit = 1;
427
else
428
{
429
permit = 0;
430
sfprintf(state->tmp, "%s/%s/%s", state->pwd, file, AT_DENY_FILE);
431
if (!(path = sfstruse(state->tmp)))
432
error(ERROR_SYSTEM|3, "out of space");
433
pathcanon(path, 0, 0);
434
sp = sfopen(NiL, path, "r");
435
}
436
437
/*
438
* reset the queue and associated user access
439
*/
440
441
for (que = (Queue_t*)dtfirst(state->table.queue.handle); que; que = (Queue_t*)dtnext(state->table.queue.handle, que))
442
if (!que->specific)
443
{
444
que->allow = permit;
445
dtwalk(que->owner, allow, que);
446
}
447
448
/*
449
* scan and update the access
450
*/
451
452
if (sp)
453
{
454
error(0, "scan %s access list", path);
455
while (s = sfgetr(sp, '\n', 1))
456
{
457
usr = user(state, s, 0);
458
for (que = (Queue_t*)dtfirst(state->table.queue.handle); que; que = (Queue_t*)dtnext(state->table.queue.handle, que))
459
if (!que->specific)
460
{
461
if (!(own = (Owner_t*)dtmatch(que->owner, &usr)))
462
{
463
if (!(own = newof(0, Owner_t, 1, 0)))
464
error(ERROR_SYSTEM|3, "out of space [queue owner]");
465
own->user = usr;
466
dtinsert(que->owner, own);
467
}
468
own->allow = que->allow;
469
}
470
}
471
sfclose(sp);
472
}
473
}
474
}
475
476
/*
477
* add a client connection
478
*/
479
480
static int
481
client(Css_t* css, Cssfd_t* fp, Csid_t* id, char** args, Cssdisc_t* disc)
482
{
483
register State_t* state = (State_t*)disc;
484
register Connection_t* con = state->con + fp->fd;
485
486
NoP(args);
487
con->id = *id;
488
return con->fd = fp->fd;
489
}
490
491
/*
492
* insert a job into the run list
493
*/
494
495
static void
496
submit(State_t* state, register Job_t* job)
497
{
498
dtinsert(state->table.job.handle, job);
499
job->owner->pending++;
500
job->owner->user->pending++;
501
job->queue->pending++;
502
state->pending++;
503
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);
504
}
505
506
/*
507
* remove job from the run list
508
*/
509
510
static void
511
complete(State_t* state, register Job_t* job)
512
{
513
dtdelete(state->table.job.handle, job);
514
if (job->pid)
515
{
516
dtdelete(state->table.pid.handle, job);
517
job->pid = 0;
518
}
519
if (job->owner)
520
{
521
if (job->run)
522
{
523
job->owner->running--;
524
job->owner->user->running--;
525
}
526
job->owner->pending--;
527
job->owner->user->pending--;
528
}
529
if (job->queue)
530
{
531
if (job->run)
532
{
533
job->queue->running--;
534
state->running--;
535
}
536
job->queue->pending--;
537
state->pending--;
538
}
539
job->run = 0;
540
}
541
542
/*
543
* drop a job
544
*/
545
546
static void
547
drop(register State_t* state, register Job_t* job)
548
{
549
complete(state, job);
550
error(0, "%s %s que %s drop \"%s\"", job->name, fmtuid(job->owner->user->uid), job->queue->name, job->label);
551
remove(job->name);
552
free(job);
553
}
554
555
/*
556
* pass job to atx for impersonation and execution
557
*/
558
559
static unsigned long
560
execute(register State_t* state, register Job_t* job)
561
{
562
Proc_t* proc;
563
unsigned long pid;
564
char* argv[4];
565
long ops[3];
566
567
argv[0] = state->atx;
568
argv[1] = job->shell;
569
argv[2] = job->name;
570
argv[3] = 0;
571
ops[0] = PROC_SYS_PGRP(1);
572
ops[1] = 0;
573
message((-2, "%s %s %s", argv[0], argv[1], argv[2]));
574
if (!(proc = procopen(argv[0], argv, NiL, ops, 0)))
575
return 0;
576
pid = proc->pid;
577
procfree(proc);
578
return pid;
579
}
580
581
/*
582
* reap the state for a job that has completed
583
*/
584
585
static void
586
reap(register State_t* state, register Job_t* job, int status)
587
{
588
char* e;
589
time_t t;
590
591
error(0, "%s %s %lu exit %d \"%s\"", job->name, fmtuid(job->owner->user->uid), job->pid, status, job->label);
592
if (job->repeat && (t = job->start) && (t = tmdate(job->repeat, &e, &t)) && !*e)
593
{
594
complete(state, job);
595
job->start = t;
596
submit(state, job);
597
}
598
else
599
drop(state, job);
600
schedule(state);
601
}
602
603
/*
604
* execute ready jobs
605
*/
606
607
static int
608
schedule(register State_t* state)
609
{
610
register Job_t* job;
611
register Queue_t* que;
612
Csstat_t st;
613
614
unsigned long x = SMAX;
615
616
csstat(state->css->state, NiL, &st);
617
for (job = (Job_t*)dtfirst(state->table.job.handle); job; job = (Job_t*)dtnext(state->table.job.handle, job))
618
{
619
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" : ""));
620
if (!job->run)
621
{
622
if (job->start <= cs.time)
623
{
624
que = job->queue;
625
if ((que->nproc <= 0 || que->running < que->nproc) &&
626
(que->load <= 0 || st.load < que->load) &&
627
(state->nproc <= 0 || state->running < state->nproc) &&
628
(que->peruser <= 0 || job->owner->running < que->peruser) &&
629
(state->peruser <= 0 || job->owner->running < state->peruser))
630
{
631
if (job->pid = execute(state, job))
632
{
633
job->run = cs.time;
634
job->owner->running++;
635
job->owner->total++;
636
job->owner->user->running++;
637
job->owner->user->total++;
638
que->running++;
639
que->total++;
640
state->running++;
641
state->total++;
642
dtinsert(state->table.pid.handle, job);
643
message((-2, "exec job=%s pid=%lu", job->name, job->pid));
644
error(0, "%s %s %lu exec \"%s\"", job->name, fmtuid(job->owner->user->uid), job->pid, job->label);
645
}
646
else if (x > (job->start + que->wait))
647
x = job->start + que->wait;
648
}
649
else if (x > (job->start + que->wait))
650
x = job->start + que->wait;
651
}
652
else
653
{
654
if (x > job->start)
655
x = job->start;
656
break;
657
}
658
}
659
}
660
if (x == SMAX)
661
state->disc.wakeup = 0;
662
else if (x > cs.time)
663
{
664
x -= cs.time;
665
x = (x >= MSMAX) ? MSMAX : (x * 1000);
666
state->disc.wakeup = x;
667
message((-2, "wakeup %s", fmtelapsed(x, 1000)));
668
}
669
return 0;
670
}
671
672
static int
673
exception(Css_t* css, unsigned long op, unsigned long arg, Cssdisc_t* disc)
674
{
675
register State_t* state = (State_t*)disc;
676
int status;
677
pid_t pid;
678
Job_t* job;
679
680
switch (op)
681
{
682
case CSS_INTERRUPT:
683
if (arg != SIGCHLD)
684
error(ERROR_SYSTEM|3, "%s: interrupt exit", fmtsignal(arg));
685
for (;;)
686
{
687
switch (pid = waitpid(-1, &status, WNOHANG))
688
{
689
case -1:
690
if (errno == EINTR)
691
continue;
692
break;
693
case 0:
694
break;
695
default:
696
status = WIFSIGNALED(status) ?
697
EXIT_TERM(WTERMSIG(status)) :
698
EXIT_CODE(WEXITSTATUS(status));
699
message((-2, "wait pid=%lu status=%d", pid, status));
700
if (job = (Job_t*)dtmatch(state->table.pid.handle, &pid))
701
reap(state, job, status);
702
continue;
703
}
704
break;
705
}
706
return 1;
707
case CSS_WAKEUP:
708
schedule(state);
709
return 1;
710
}
711
error(ERROR_SYSTEM|3, "poll error");
712
return -1;
713
}
714
715
/*
716
* list owner names in handle
717
*/
718
719
static int
720
listowner(Dt_t* dt, void* object, void* handle)
721
{
722
register Owner_t* own = (Owner_t*)object;
723
register Visit_t* vis = (Visit_t*)handle;
724
725
NoP(dt);
726
if (own->allow == vis->queue->allow)
727
sfprintf(vis->state->tmp, " %s", own->user->name);
728
return 0;
729
}
730
731
/*
732
* list queue status
733
*/
734
735
static int
736
listqueue(Dt_t* dt, void* object, void* handle)
737
{
738
register Queue_t* que = (Queue_t*)object;
739
Connection_t* con = ((Visit_t*)handle)->con;
740
State_t* state = ((Visit_t*)handle)->state;
741
char* s;
742
Visit_t visit;
743
744
NoP(dt);
745
sfprintf(state->tmp, " %c", que->allow ? '+' : '-');
746
visit.queue = que;
747
visit.state = state;
748
dtwalk(que->owner, listowner, &visit);
749
if (!(s = sfstruse(state->tmp)))
750
error(ERROR_SYSTEM|3, "out of space");
751
if (!s[2])
752
*s = 0;
753
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);
754
return 0;
755
}
756
757
/*
758
* output name='value'
759
*/
760
761
static void
762
neqv(register Sfio_t* sp, const char* name, register char* v)
763
{
764
register int c;
765
766
sfprintf(sp, " \\\n %s='", name);
767
while (c = *v++)
768
{
769
if (c == '\'')
770
sfputr(sp, "'\\'", -1);
771
sfputc(sp, c);
772
}
773
sfputc(sp, '\'');
774
}
775
776
/*
777
* execute the at command in s
778
*/
779
780
static int
781
command(register State_t* state, Connection_t* con, register char* s, int n, char* data)
782
{
783
register Queue_t* que;
784
register Job_t* job;
785
Owner_t* own;
786
User_t* usr;
787
char* t;
788
char* u;
789
char* b;
790
char* h;
791
int c;
792
int admin;
793
int mail;
794
int skip;
795
unsigned long m;
796
unsigned long w;
797
long x;
798
Job_t* next;
799
Sfio_t* sp;
800
Visit_t visit;
801
802
usr = user(state, NiL, con->id.uid);
803
b = s;
804
admin = 0;
805
if (*++s == AT_ADMIN)
806
{
807
s++;
808
if (!usr->admin)
809
goto denied;
810
if (!++usr->admin)
811
usr->admin = 1;
812
admin = 1;
813
}
814
if (*s == AT_QUEUE)
815
{
816
t = ++s;
817
if (!(s = strchr(s, ' ')))
818
{
819
error(ERROR_OUTPUT|2, con->fd, "%s: invalid queue name", t);
820
return -1;
821
}
822
*s = 0;
823
if (!(que = (Queue_t*)dtmatch(state->table.queue.handle, t)))
824
{
825
error(ERROR_OUTPUT|2, con->fd, "%s: unknown queue", t);
826
return -1;
827
}
828
*s++ = ' ';
829
if (!permit(que, usr))
830
goto noqueue;
831
}
832
else
833
que = 0;
834
if (mail = *s == AT_MAIL)
835
s++;
836
switch (c = *s++)
837
{
838
case AT_ACCESS:
839
if (!que && !permit(que = state->queue, usr))
840
goto noqueue;
841
break;
842
case AT_DEBUG:
843
if (!usr->admin)
844
goto denied;
845
if (!++usr->admin)
846
usr->admin = 1;
847
error_info.trace = -strtol(s, &t, 0);
848
message((error_info.trace, "%s", fmtident(id)));
849
break;
850
case AT_INFO:
851
error(ERROR_OUTPUT|0, con->fd, "at service daemon pid %ld user %s", state->con[0].id.pid, fmtuid(state->admin[0]));
852
error(ERROR_OUTPUT|0, con->fd, "QUE TOTAL SUB RUN MAX USR PRI LOAD WAIT ACCESS");
853
visit.state = state;
854
visit.con = con;
855
if (que)
856
listqueue(NiL, que, &visit);
857
else
858
dtwalk(state->table.queue.handle, listqueue, &visit);
859
break;
860
case AT_JOB:
861
if (!que)
862
que = state->queue;
863
if (!(own = permit(que, usr)))
864
goto noqueue;
865
if (own->user->pending >= HOG)
866
{
867
error(ERROR_OUTPUT|2, con->fd, "%s: hog", own->user->name);
868
return -1;
869
}
870
skip = strtol(s, &t, 10);
871
if (*t++ != ' ' || !state->init && skip > n)
872
{
873
error(ERROR_OUTPUT|2, con->fd, "garbled job message");
874
return -1;
875
}
876
for (s = t; *s && *s != '\n'; s++);
877
h = s + 1;
878
if (!(job = newof(0, Job_t, 1, s - t)))
879
{
880
error(ERROR_SYSTEM|ERROR_OUTPUT|2, con->fd, "out of space [job]");
881
return -1;
882
}
883
if (state->sequence < cs.time)
884
state->sequence = cs.time;
885
if (data)
886
sfsprintf(job->name, sizeof(job->name), "%s", data);
887
else
888
sfsprintf(job->name, sizeof(job->name), "%..36lu.%..36lu.%..36lu", con->id.uid, con->id.gid, state->sequence++);
889
s = (char*)memcpy(job->data, t, s - t);
890
job->start = cs.time;
891
job->shell = s;
892
while (*s)
893
if (*s++ == ' ')
894
{
895
*(s - 1) = 0;
896
break;
897
}
898
job->id = con->id;
899
job->queue = que;
900
job->owner = own;
901
job->mail = mail;
902
while (c = *s++)
903
{
904
switch (c)
905
{
906
case AT_LABEL:
907
t = job->label;
908
for (t = job->label; *s && !isspace(*s); s++)
909
if (t < &job->label[sizeof(job->label)-1])
910
*t++ = *s;
911
if (*s)
912
s++;
913
continue;
914
case AT_TIME:
915
job->start = strtol(s, &t, 0);
916
s = t;
917
if (*s == ' ')
918
s++;
919
if (*s)
920
{
921
job->repeat = s;
922
if (*s == '+')
923
{
924
while (*++s && !isspace(*s));
925
while (isspace(*s))
926
s++;
927
}
928
}
929
job->period = s;
930
break;
931
default:
932
break;
933
}
934
break;
935
}
936
if (state->init && job->repeat)
937
job->start = tmdate(job->repeat, NiL, NiL);
938
if (!state->init && !*(t = job->label))
939
{
940
m = 0;
941
t = b + skip;
942
if (*t == ':')
943
{
944
while (isspace(*++t));
945
if (u = strchr(t, ';'))
946
{
947
while (u > t && isspace(*(u - 1)))
948
u--;
949
m = u - t;
950
}
951
}
952
if (m)
953
{
954
if (m >= sizeof(job->label))
955
m = sizeof(job->label) - 1;
956
memcpy(job->label, t, m);
957
job->label[m] = 0;
958
}
959
else
960
{
961
for (t = b + skip; *t; t++)
962
if (isalnum(*t))
963
{
964
u = t;
965
while (isalnum(*++t));
966
c = t - u;
967
if (c == 3 && strneq(u, "for", 3) || c == 2 && strneq(u, "if", 2) || c == 5 && strneq(u, "while", 5))
968
continue;
969
break;
970
}
971
m = 0;
972
x = -1;
973
while (c = *t++)
974
{
975
if (isalnum(c))
976
{
977
if (x)
978
{
979
if (x > 0)
980
{
981
w = sfstrtell(state->tmp);
982
if (++m >= sizeof(job->label))
983
break;
984
sfputc(state->tmp, '.');
985
}
986
x = 0;
987
}
988
if (++m >= sizeof(job->label))
989
break;
990
sfputc(state->tmp, c);
991
}
992
else if (!x)
993
x = 1;
994
}
995
if (m >= sizeof(job->label))
996
sfstrseek(state->tmp, w, SEEK_SET);
997
if (!(t = sfstruse(state->tmp)))
998
error(ERROR_SYSTEM|3, "out of space");
999
strcpy(job->label, t);
1000
}
1001
}
1002
submit(state, job);
1003
if (!state->init)
1004
{
1005
if (!(sp = sfopen(NiL, job->name, "w")))
1006
goto noexec;
1007
chmod(job->name, AT_JOB_MODE);
1008
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);
1009
if (job->repeat)
1010
sfprintf(sp, " %s", job->repeat);
1011
sfputc(sp, '\n');
1012
c = h - b;
1013
b += c;
1014
n -= c;
1015
skip -= c;
1016
sfprintf(sp, "tmp=/tmp/at$$\ntrap \"rm -f $tmp\" 0 1 2 3 15\n");
1017
sfprintf(sp, "{\necho at job %s exec $(date)\n{\n", job->name);
1018
sfprintf(sp, "export");
1019
x = 0;
1020
for (c = 0; c < elementsof(export); c++)
1021
if ((s = getenv(export[c])) && *s)
1022
{
1023
neqv(sp, export[c], s);
1024
x = 1;
1025
}
1026
if (que->home)
1027
{
1028
b += skip;
1029
n -= skip;
1030
neqv(sp, "HOME", job->owner->user->home);
1031
neqv(sp, "LOGNAME", job->owner->user->name);
1032
neqv(sp, "USER", job->owner->user->name);
1033
neqv(sp, "PATH", pathbin());
1034
neqv(sp, "SHELL", job->shell);
1035
sfputr(sp, "\ncd \"$HOME\"\n", -1);
1036
x = 1;
1037
}
1038
sfputr(sp, x ? "" : " HOME", '\n');
1039
b[n - 1] = '\n';
1040
if (sfwrite(sp, b, n) != n)
1041
goto noexec;
1042
sfprintf(sp, "\n} </dev/null\necho at job %s exit $(date) status $?\n} >$tmp 2>&1\n", job->name);
1043
if (!job->mail)
1044
sfprintf(sp, "test \"$(wc -l < $tmp)\" -le 2 || ");
1045
sfprintf(sp, "mailx -s \"at job status\" %s < $tmp\n", job->owner->user->name);
1046
if (sfclose(sp))
1047
{
1048
sp = 0;
1049
goto noexec;
1050
}
1051
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);
1052
schedule(state);
1053
}
1054
break;
1055
case AT_LIST:
1056
case AT_REMOVE:
1057
case AT_STATUS:
1058
if (*s++ != ' ')
1059
s = 0;
1060
m = 0;
1061
for (job = (Job_t*)dtfirst(state->table.job.handle); job; job = next)
1062
{
1063
next = (Job_t*)dtnext(state->table.job.handle, job);
1064
if ((!que || que == job->queue) && (s && strmatch(job->name, s) || !s && (admin || con->id.uid == job->id.uid)) && job->owner->allow)
1065
switch (c)
1066
{
1067
case AT_LIST:
1068
error(ERROR_OUTPUT|0, con->fd, "%s\t%s", job->name, fmttime("%a %b %e %T %Y", job->start));
1069
break;
1070
case AT_STATUS:
1071
if (!m++)
1072
error(ERROR_OUTPUT|0, con->fd, "JOB LABEL PID Q USER START REPEAT");
1073
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);
1074
break;
1075
case AT_REMOVE:
1076
if (con->id.uid == job->id.uid || !con->id.uid)
1077
{
1078
drop(state, job);
1079
m++;
1080
}
1081
else
1082
error(ERROR_OUTPUT|2, con->fd, "%s: only %s can remove this job", job->name, fmtuid(job->id.uid));
1083
break;
1084
}
1085
}
1086
if (s && !m)
1087
{
1088
error(ERROR_OUTPUT|0, con->fd, "%s: no matching jobs", s ? s : "*");
1089
return -1;
1090
}
1091
break;
1092
case AT_LOG:
1093
sfprintf(state->tmp, "%s/%s", state->pwd, AT_LOG_FILE);
1094
if (!(s = sfstruse(state->tmp)))
1095
error(ERROR_SYSTEM|3, "out of space");
1096
pathcanon(s, 0, 0);
1097
error(ERROR_OUTPUT|0, con->fd, "%s", s);
1098
break;
1099
case AT_QUIT:
1100
if (!usr->admin)
1101
goto denied;
1102
if (!++usr->admin)
1103
usr->admin = 1;
1104
error(0, "daemon quit by %s", usr->name);
1105
exit(0);
1106
break;
1107
case AT_UPDATE:
1108
if (!usr->admin)
1109
goto denied;
1110
if (!++usr->admin)
1111
usr->admin = 1;
1112
if (*s)
1113
queue(state, s);
1114
update(state);
1115
break;
1116
case AT_USER:
1117
error(ERROR_OUTPUT|0, con->fd, "USER ADMIN TOTAL SUB RUN HOME");
1118
for (usr = (User_t*)dtfirst(state->table.user.handle); usr; usr = (User_t*)dtnext(state->table.user.handle, usr))
1119
if (admin || con->id.uid == usr->uid)
1120
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);
1121
break;
1122
case AT_VERSION:
1123
error(ERROR_OUTPUT|0, con->fd, "%s", fmtident(id));
1124
break;
1125
default:
1126
error(ERROR_OUTPUT|2, con->fd, "%c: unknown command", *(s - 1));
1127
return -1;
1128
}
1129
return 0;
1130
denied:
1131
error(ERROR_OUTPUT|2, con->fd, "%s: access denied", usr->name);
1132
return -1;
1133
noexec:
1134
if (sp)
1135
sfclose(sp);
1136
error(ERROR_SYSTEM|ERROR_OUTPUT|2, con->fd, "%s: cannot save action", job->name);
1137
drop(state, job);
1138
return -1;
1139
noqueue:
1140
error(ERROR_OUTPUT|2, con->fd, "%s: queue %s access denied", usr->name, que->name);
1141
return -1;
1142
}
1143
1144
/*
1145
* order job by <start,name>
1146
*/
1147
1148
static int
1149
order(Dt_t* dt, void* a, void* b, Dtdisc_t* disc)
1150
{
1151
register long r1;
1152
register int r2;
1153
1154
NoP(dt);
1155
NoP(disc);
1156
if (!(r2 = strcmp((char*)a + sizeof(unsigned long), (char*)b + sizeof(unsigned long))))
1157
return 0;
1158
if (!(r1 = *(unsigned long*)a - *(unsigned long*)b))
1159
return r2;
1160
return r1;
1161
}
1162
1163
static unsigned long rollover; /* XXX stampwrite() has no discipline */
1164
1165
/*
1166
* commit to the log file
1167
* if its too big then rename to .old and start fresh
1168
*/
1169
1170
static void
1171
commit(void)
1172
{
1173
int rolled;
1174
int fd;
1175
time_t t;
1176
unsigned long now;
1177
struct stat st;
1178
char buf[PATH_MAX];
1179
Tm_t* tm;
1180
1181
static int commiting = 0;
1182
1183
if (commiting++)
1184
{
1185
commiting--;
1186
return;
1187
}
1188
now = NOW;
1189
if (!rollover)
1190
{
1191
t = stat(AT_LOG_FILE, &st) ? now : st.st_mtime;
1192
tm = tmmake(&t);
1193
tm->tm_mon++;
1194
tm->tm_mday = 1;
1195
tm->tm_hour = 0;
1196
tm->tm_min = 0;
1197
tm->tm_sec = 0;
1198
rollover = tmtime(tm, TM_LOCALZONE);
1199
}
1200
if (now >= rollover)
1201
{
1202
rolled = 1;
1203
error(0, "log file rollover");
1204
sfsprintf(buf, sizeof(buf), "%s.old", AT_LOG_FILE);
1205
remove(buf);
1206
if (rename(AT_LOG_FILE, buf))
1207
error(ERROR_SYSTEM|AT_STRICT, "%s: cannot rename log file to %s", AT_LOG_FILE, buf);
1208
t = rollover;
1209
tm = tmmake(&t);
1210
tm->tm_mon++;
1211
rollover = tmtime(tm, TM_LOCALZONE);
1212
}
1213
else
1214
rolled = 0;
1215
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)))
1216
error(ERROR_SYSTEM|AT_STRICT, "%s: cannot append to log file", AT_LOG_FILE);
1217
if (rolled)
1218
error(0, "log file rollover");
1219
commiting--;
1220
}
1221
1222
/*
1223
* prepend current date-time to buffer on fd==2
1224
* and drop the initial command label if any
1225
*/
1226
1227
static ssize_t
1228
stampwrite(int fd, const void* buf, size_t n)
1229
{
1230
register char* s;
1231
register int i;
1232
register ssize_t r;
1233
register ssize_t z;
1234
unsigned long now;
1235
1236
r = 0;
1237
if (rollover)
1238
{
1239
now = NOW;
1240
if (now >= rollover)
1241
commit();
1242
}
1243
else if (!now)
1244
now = NOW;
1245
if (fd == 2 && (s = fmttime(AT_TIME_FORMAT, now)))
1246
{
1247
i = strlen(s);
1248
s[i++] = ' ';
1249
if ((z = write(fd, s, i)) < 0)
1250
r = -1;
1251
else
1252
r += z;
1253
for (s = (char*)buf; s < ((char*)buf + n - 1) && !isspace(*s); s++)
1254
if (*s == ':')
1255
{
1256
while (++s < ((char*)buf + n - 1) && isspace(*s));
1257
n -= s - (char*)buf;
1258
buf = (void*)s;
1259
break;
1260
}
1261
}
1262
if ((z = write(fd, buf, n)) < 0)
1263
r = -1;
1264
else if (r >= 0)
1265
r += z;
1266
return r;
1267
}
1268
1269
/*
1270
* service a request
1271
*/
1272
1273
static int
1274
request(Css_t* css, Cssfd_t* fp, Cssdisc_t* disc)
1275
{
1276
register State_t* state = (State_t*)disc;
1277
register Connection_t* con = state->con + fp->fd;
1278
register char* s;
1279
char* t;
1280
unsigned long n;
1281
int c;
1282
1283
if (fp->status != CS_POLL_READ)
1284
return -1;
1285
if ((c = csread(state->css->state, fp->fd, state->buf, 7, CS_EXACT|CS_RESTART)) != 7)
1286
{
1287
if (c)
1288
error(ERROR_OUTPUT|2, con->fd, "message size read error");
1289
return -1;
1290
}
1291
state->buf[6] = 0;
1292
if ((n = strtol(state->buf + 1, &t, 10)) <= 0 || *t)
1293
{
1294
error(ERROR_OUTPUT|2, con->fd, "invalid message size");
1295
return -1;
1296
}
1297
if (n > state->bufsiz)
1298
{
1299
if (n > INT_MAX || (c = roundof(n, PATH_MAX)) <= 0 || !(s = newof(state->buf, char, c, 0)))
1300
{
1301
error(ERROR_OUTPUT|2, con->fd, "%ld: message too big", n);
1302
return -1;
1303
}
1304
state->buf = s;
1305
state->bufsiz = c;
1306
}
1307
if (csread(state->css->state, fp->fd, s = state->buf, n, CS_EXACT|CS_RESTART) != n)
1308
{
1309
error(ERROR_OUTPUT|2, con->fd, "message body read error");
1310
return -1;
1311
}
1312
s[n - 1] = 0;
1313
command(state, con, s, n, NiL);
1314
return -1;
1315
}
1316
1317
/*
1318
* initialize the state
1319
*/
1320
1321
static int
1322
init(const char* path)
1323
{
1324
register State_t* state;
1325
register DIR* dir;
1326
register struct dirent* ent;
1327
char* s;
1328
char* b;
1329
Sfio_t* sp;
1330
int i;
1331
unsigned long limited;
1332
unsigned long* ap;
1333
struct stat ds;
1334
struct stat hs;
1335
struct stat js;
1336
struct stat xs;
1337
1338
umask(S_IWGRP|S_IWOTH);
1339
if ((i = (int)strtol(astconf("OPEN_MAX", NiL, NiL), NiL, 0)) < 20)
1340
i = 20;
1341
if (!(state = newof(0, State_t, 1, (i - 1) * sizeof(Connection_t))))
1342
error(ERROR_SYSTEM|3, "out of space [state]");
1343
state->bufsiz = 4 * PATH_MAX;
1344
if (!(state->buf = newof(0, char, state->bufsiz, 0)))
1345
error(ERROR_SYSTEM|3, "out of space [buf]");
1346
if (!(state->tmp = sfstropen()))
1347
error(ERROR_SYSTEM|3, "out of space [tmp]");
1348
ap = state->admin;
1349
*ap++ = state->con[0].id.uid = geteuid();
1350
*ap++ = 0;
1351
s = state->buf;
1352
if (!pathpath(AT_JOB_DIR, "", PATH_ABSOLUTE|PATH_EXECUTE, s, state->bufsiz) || lstat(s, &ds) || !AT_DIR_OK(&ds))
1353
error(ERROR_SYSTEM|3, "%s: job directory not found", AT_JOB_DIR);
1354
if (ds.st_uid != state->admin[0])
1355
error(ERROR_SYSTEM|3, "%s: job directory uid %d != effective uid %d", s, ds.st_uid, state->admin[0]);
1356
state->disc.version = CSS_VERSION;
1357
state->disc.flags = CSS_DAEMON|CSS_ERROR|CSS_INTERRUPT|CSS_WAKEUP;
1358
state->disc.errorf = errorf;
1359
state->disc.acceptf = client;
1360
state->disc.actionf = request;
1361
state->disc.exceptf = exception;
1362
if (!(state->css = cssopen(path, &state->disc)))
1363
return -1;
1364
state->con[0].id.gid = getegid();
1365
state->con[0].id.pid = getpid();
1366
state->con[0].id.hid = csaddr(state->css->state, NiL);
1367
state->con[0].fd = 2;
1368
state->init = 1;
1369
b = s + strlen(s);
1370
*b++ = '/';
1371
sfsprintf(b, state->bufsiz - (b - s), "%s", csname(state->css->state, 0L));
1372
if (lstat(s, &hs) && (mkdir(s, AT_DIR_MODE) || chmod(s, AT_DIR_MODE) || lstat(s, &hs)))
1373
error(ERROR_SYSTEM|3, "%s: cannot create job host directory", s);
1374
if (!AT_DIR_OK(&hs) || ds.st_uid != hs.st_uid || chdir(s))
1375
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);
1376
if (!(state->pwd = strdup(s)))
1377
error(ERROR_SYSTEM|3, "out of space [pwd]");
1378
if (hs.st_uid != state->admin[0])
1379
error(AT_STRICT, "%s: directory owner %s does not match daemon %s", s, fmtuid(hs.st_uid), fmtuid(state->admin[0]));
1380
sfsprintf(s, state->bufsiz, "%s/%s", state->pwd, AT_EXEC_FILE);
1381
pathcanon(s, 0, 0);
1382
if (lstat(s, &xs))
1383
error(ERROR_SYSTEM|3, "%s: job exec command not found", s);
1384
if (!S_ISREG(xs.st_mode))
1385
error(3, "%s: invalid mode %s -- regular file expected", s, fmtmode(xs.st_mode, 0));
1386
if ((xs.st_mode&(S_IXUSR|S_IXGRP|S_IWOTH|S_IXOTH)) != (S_IXUSR|S_IXGRP|S_IXOTH))
1387
error(3, "%s: invalid mode %s", s, fmtmode(xs.st_mode, 0));
1388
if (!(xs.st_mode&S_ISUID) && geteuid() != 0 && geteuid() != xs.st_uid)
1389
error(3, "%s: invalid euid %d -- %d expected", s, geteuid(), xs.st_uid);
1390
#if 0
1391
if (!AT_EXEC_OK(&ds, &xs))
1392
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));
1393
#endif
1394
*ap++ = ds.st_uid;
1395
*ap = xs.st_uid;
1396
limited = (xs.st_mode & S_ISUID) ? (unsigned long)xs.st_uid : state->admin[0];
1397
if (!(state->atx = strdup(s)))
1398
error(ERROR_SYSTEM|3, "out of space [atx]");
1399
state->table.job.discipline.key = offsetof(Job_t, start);
1400
state->table.job.discipline.comparf = order;
1401
if (!(state->table.job.handle = dtopen(&state->table.job.discipline, Dtoset)))
1402
error(ERROR_SYSTEM|3, "out of space [job table]");
1403
state->table.owner.discipline.key = offsetof(Owner_t, user);
1404
state->table.owner.discipline.size = sizeof(User_t*);
1405
state->table.pid.discipline.key = offsetof(Job_t, pid);
1406
state->table.pid.discipline.size = sizeof(long);
1407
state->table.pid.discipline.link = offsetof(Job_t, bypid);
1408
if (!(state->table.pid.handle = dtopen(&state->table.pid.discipline, Dtset)))
1409
error(ERROR_SYSTEM|3, "out of space [pid table]");
1410
state->table.queue.discipline.key = offsetof(Queue_t, name);
1411
if (!(state->table.queue.handle = dtopen(&state->table.queue.discipline, Dtoset)))
1412
error(ERROR_SYSTEM|3, "out of space [queue table]");
1413
state->table.uid.discipline.key = offsetof(User_t, uid);
1414
state->table.uid.discipline.size = sizeof(long);
1415
state->table.uid.discipline.link = offsetof(User_t, byuid);
1416
if (!(state->table.uid.handle = dtopen(&state->table.uid.discipline, Dtset)))
1417
error(ERROR_SYSTEM|3, "out of space [uid table]");
1418
state->table.user.discipline.key = offsetof(User_t, name);
1419
if (!(state->table.user.handle = dtopen(&state->table.user.discipline, Dtoset)))
1420
error(ERROR_SYSTEM|3, "out of space [user table]");
1421
for (i = 0; i < elementsof(queuedefs); i++)
1422
queue(state, strcpy(state->buf, queuedefs[i]));
1423
commit();
1424
error(0, "daemon restart pid %ld user %s", state->con[0].id.pid, fmtuid(state->admin[0]));
1425
if (limited)
1426
error(0, "service limited to user %s", fmtuid(limited));
1427
1428
/*
1429
* update the queue definitions
1430
*/
1431
1432
update(state);
1433
1434
/*
1435
* resubmit old jobs
1436
*/
1437
1438
if (dir = opendir("."))
1439
{
1440
while (ent = readdir(dir))
1441
if (lstat(s = ent->d_name, &js))
1442
{
1443
error(0, "cannot stat old job %s", s);
1444
remove(s);
1445
}
1446
else if (!S_ISREG(js.st_mode))
1447
{
1448
if (!streq(s, ".") && !streq(s, ".."))
1449
{
1450
error(0, "invalid old job %s type %s rejected", s, fmtmode(js.st_mode, 0));
1451
if (S_ISDIR(js.st_mode))
1452
rmdir(s);
1453
else
1454
remove(s);
1455
}
1456
}
1457
else if (sfsscanf(s, "%..36lu.%..36lu.%..36lu", &state->con[0].id.uid, &state->con[0].id.gid, &cs.time) != 3)
1458
{
1459
error(0, "invalid old job %s name rejected", s);
1460
remove(s);
1461
}
1462
else if (!AT_OLD_OK(&hs, &js))
1463
{
1464
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);
1465
remove(s);
1466
}
1467
else if (!(sp = sfopen(NiL, s, "r")))
1468
{
1469
error(0, "cannot read old job %s", s);
1470
remove(s);
1471
}
1472
else
1473
{
1474
if (b = (char*)sfreserve(sp, SF_UNBOUND, SF_LOCKR))
1475
command(state, state->con, b, sfvalue(sp), s);
1476
sfclose(sp);
1477
}
1478
closedir(dir);
1479
}
1480
CSTIME();
1481
state->con[0].id.uid = geteuid();
1482
state->con[0].id.gid = getegid();
1483
state->init = 0;
1484
schedule(state);
1485
return 0;
1486
}
1487
1488
int
1489
main(int argc, char** argv)
1490
{
1491
char* path;
1492
pid_t pid;
1493
int status;
1494
1495
NoP(argc);
1496
NoP(argv);
1497
error_info.id = "at.svc";
1498
error_info.write = stampwrite;
1499
if ((path = argv[1]) && !(path = strdup(path)))
1500
path = argv[1];
1501
1502
/*
1503
* monitor the daemon and restart if it dies
1504
*/
1505
1506
csdaemon(&cs, (1<<0)|(1<<1)|(1<<2));
1507
for (;;)
1508
{
1509
if ((pid = fork()) <= 0)
1510
{
1511
if (!init(path))
1512
csspoll(CS_NEVER, 0);
1513
return 1;
1514
}
1515
while (waitpid(pid, &status, 0) != pid);
1516
if (!status)
1517
break;
1518
sleep(60);
1519
}
1520
return 0;
1521
}
1522
1523