1433d6423SLionel Sambuc /* at - run a command at a specified time Author: Jan Looyen */
2433d6423SLionel Sambuc
3433d6423SLionel Sambuc #include <sys/types.h>
4433d6423SLionel Sambuc #include <time.h>
5433d6423SLionel Sambuc #include <fcntl.h>
6433d6423SLionel Sambuc #include <unistd.h>
7433d6423SLionel Sambuc #include <stdlib.h>
8433d6423SLionel Sambuc #include <string.h>
9433d6423SLionel Sambuc #include <sys/stat.h>
10433d6423SLionel Sambuc #include <stdio.h>
11433d6423SLionel Sambuc #include <limits.h>
12433d6423SLionel Sambuc #include <signal.h>
13433d6423SLionel Sambuc #include <errno.h>
14433d6423SLionel Sambuc
15433d6423SLionel Sambuc #define STARTDAY 0 /* see ctime(3) */
16433d6423SLionel Sambuc #define LEAPDAY (STARTDAY+59)
17433d6423SLionel Sambuc #define MAXDAYNR (STARTDAY+365)
18433d6423SLionel Sambuc #define NODAY (-2)
19433d6423SLionel Sambuc char CRONPID[] = "/usr/run/cron.pid";
20433d6423SLionel Sambuc
21433d6423SLionel Sambuc int main(int argc, char **argv, char **envp);
22433d6423SLionel Sambuc int getltim(char *t);
23433d6423SLionel Sambuc int getlday(char *m, char *d);
24433d6423SLionel Sambuc int digitstring(char *s);
25433d6423SLionel Sambuc
main(int argc,char ** argv,char ** envp)26433d6423SLionel Sambuc int main(int argc, char **argv, char **envp)
27433d6423SLionel Sambuc {
28433d6423SLionel Sambuc int i, c, mask, ltim, year, lday = NODAY;
29433d6423SLionel Sambuc char buf[64], job[30], pastjob[35], *dp, *sp;
30433d6423SLionel Sambuc struct tm *p;
31*d0055759SDavid van Moolenbroek time_t clk;
32433d6423SLionel Sambuc FILE *fp;
33433d6423SLionel Sambuc char pwd[PATH_MAX+1];
34433d6423SLionel Sambuc
35433d6423SLionel Sambuc /*-------------------------------------------------------------------------*
36433d6423SLionel Sambuc * check arguments & pipe to "pwd" *
37433d6423SLionel Sambuc *-------------------------------------------------------------------------*/
38433d6423SLionel Sambuc if (argc < 2 || argc > 5) {
39433d6423SLionel Sambuc fprintf(stderr, "Usage: %s time [month day] [file]\n", argv[0]);
40433d6423SLionel Sambuc exit(1);
41433d6423SLionel Sambuc }
42433d6423SLionel Sambuc if ((ltim = getltim(argv[1])) == -1) {
43433d6423SLionel Sambuc fprintf(stderr, "%s: wrong time specification\n", argv[0]);
44433d6423SLionel Sambuc exit(1);
45433d6423SLionel Sambuc }
46433d6423SLionel Sambuc if ((argc == 4 || argc == 5) && (lday = getlday(argv[2], argv[3])) == -1) {
47433d6423SLionel Sambuc fprintf(stderr, "%s: wrong date specification\n", argv[0]);
48433d6423SLionel Sambuc exit(1);
49433d6423SLionel Sambuc }
50433d6423SLionel Sambuc if ((argc == 3 || argc == 5) && open(argv[argc - 1], O_RDONLY) == -1) {
51433d6423SLionel Sambuc fprintf(stderr, "%s: cannot find: %s\n", argv[0], argv[argc - 1]);
52433d6423SLionel Sambuc exit(1);
53433d6423SLionel Sambuc }
54433d6423SLionel Sambuc if (getcwd(pwd, sizeof(pwd)) == NULL) {
55433d6423SLionel Sambuc fprintf(stderr, "%s: cannot determine current directory: %s\n",
56433d6423SLionel Sambuc argv[0], strerror(errno));
57433d6423SLionel Sambuc exit(1);
58433d6423SLionel Sambuc }
59433d6423SLionel Sambuc
60433d6423SLionel Sambuc /*-------------------------------------------------------------------------*
61433d6423SLionel Sambuc * determine execution time and create 'at' job file *
62433d6423SLionel Sambuc *-------------------------------------------------------------------------*/
63433d6423SLionel Sambuc time(&clk);
64433d6423SLionel Sambuc p = localtime(&clk);
65433d6423SLionel Sambuc year = p->tm_year;
66433d6423SLionel Sambuc if (lday == NODAY) { /* no [month day] given */
67433d6423SLionel Sambuc lday = p->tm_yday;
68433d6423SLionel Sambuc if (ltim <= (p->tm_hour * 100 + p->tm_min)) {
69433d6423SLionel Sambuc lday++;
70433d6423SLionel Sambuc if ((lday == MAXDAYNR && (year % 4)) || lday == MAXDAYNR + 1) {
71433d6423SLionel Sambuc lday = STARTDAY;
72433d6423SLionel Sambuc year++;
73433d6423SLionel Sambuc }
74433d6423SLionel Sambuc }
75433d6423SLionel Sambuc } else
76433d6423SLionel Sambuc switch (year % 4) {
77433d6423SLionel Sambuc case 0:
78433d6423SLionel Sambuc if (lday < p->tm_yday ||
79433d6423SLionel Sambuc (lday == p->tm_yday &&
80433d6423SLionel Sambuc ltim <= (p->tm_hour * 100 + p->tm_min))) {
81433d6423SLionel Sambuc year++;
82433d6423SLionel Sambuc if (lday > LEAPDAY) lday--;
83433d6423SLionel Sambuc }
84433d6423SLionel Sambuc break;
85433d6423SLionel Sambuc case 1:
86433d6423SLionel Sambuc case 2:
87433d6423SLionel Sambuc if (lday > LEAPDAY) lday--;
88433d6423SLionel Sambuc if (lday < p->tm_yday ||
89433d6423SLionel Sambuc (lday == p->tm_yday &&
90433d6423SLionel Sambuc ltim <= (p->tm_hour * 100 + p->tm_min)))
91433d6423SLionel Sambuc year++;
92433d6423SLionel Sambuc break;
93433d6423SLionel Sambuc case 3:
94433d6423SLionel Sambuc if (lday < ((lday > LEAPDAY) ? p->tm_yday + 1 : p->tm_yday) ||
95433d6423SLionel Sambuc (lday ==((lday > LEAPDAY) ? p->tm_yday + 1 : p->tm_yday) &&
96433d6423SLionel Sambuc ltim <= (p->tm_hour * 100 + p->tm_min)))
97433d6423SLionel Sambuc year++;
98433d6423SLionel Sambuc else if (lday > LEAPDAY)
99433d6423SLionel Sambuc lday--;
100433d6423SLionel Sambuc break;
101433d6423SLionel Sambuc }
102433d6423SLionel Sambuc sprintf(job, "/usr/spool/at/%02d.%03d.%04d.%02d",
103433d6423SLionel Sambuc year % 100, lday, ltim, getpid() % 100);
104433d6423SLionel Sambuc sprintf(pastjob, "/usr/spool/at/past/%02d.%03d.%04d.%02d",
105433d6423SLionel Sambuc year % 100, lday, ltim, getpid() % 100);
106433d6423SLionel Sambuc mask= umask(0077);
107433d6423SLionel Sambuc if ((fp = fopen(pastjob, "w")) == NULL) {
108433d6423SLionel Sambuc fprintf(stderr, "%s: cannot create %s: %s\n",
109433d6423SLionel Sambuc argv[0], pastjob, strerror(errno));
110433d6423SLionel Sambuc exit(1);
111433d6423SLionel Sambuc }
112433d6423SLionel Sambuc
113433d6423SLionel Sambuc /*-------------------------------------------------------------------------*
114433d6423SLionel Sambuc * write environment and command(s) to 'at'job file *
115433d6423SLionel Sambuc *-------------------------------------------------------------------------*/
116433d6423SLionel Sambuc i = 0;
117433d6423SLionel Sambuc while ((sp= envp[i++]) != NULL) {
118433d6423SLionel Sambuc dp = buf;
119433d6423SLionel Sambuc while ((c= *sp++) != '\0' && c != '=' && dp < buf+sizeof(buf)-1)
120433d6423SLionel Sambuc *dp++ = c;
121433d6423SLionel Sambuc if (c != '=') continue;
122433d6423SLionel Sambuc *dp = '\0';
123433d6423SLionel Sambuc fprintf(fp, "%s='", buf);
124433d6423SLionel Sambuc while (*sp != 0) {
125433d6423SLionel Sambuc if (*sp == '\'')
126433d6423SLionel Sambuc fprintf(fp, "'\\''");
127433d6423SLionel Sambuc else
128433d6423SLionel Sambuc fputc(*sp, fp);
129433d6423SLionel Sambuc sp++;
130433d6423SLionel Sambuc }
131433d6423SLionel Sambuc fprintf(fp, "'; export %s\n", buf);
132433d6423SLionel Sambuc }
133433d6423SLionel Sambuc fprintf(fp, "cd '%s'\n", pwd);
134433d6423SLionel Sambuc fprintf(fp, "umask %o\n", mask);
135433d6423SLionel Sambuc if (argc == 3 || argc == 5)
136433d6423SLionel Sambuc fprintf(fp, "%s\n", argv[argc - 1]);
137433d6423SLionel Sambuc else /* read from stdinput */
138433d6423SLionel Sambuc while ((c = getchar()) != EOF) putc(c, fp);
139433d6423SLionel Sambuc fclose(fp);
140433d6423SLionel Sambuc
141433d6423SLionel Sambuc if (chown(pastjob, getuid(), getgid()) == -1) {
142433d6423SLionel Sambuc fprintf(stderr, "%s: cannot set ownership of %s: %s\n",
143433d6423SLionel Sambuc argv[0], pastjob, strerror(errno));
144433d6423SLionel Sambuc unlink(pastjob);
145433d6423SLionel Sambuc exit(1);
146433d6423SLionel Sambuc }
147433d6423SLionel Sambuc /* "Arm" the job. */
148433d6423SLionel Sambuc if (rename(pastjob, job) == -1) {
149433d6423SLionel Sambuc fprintf(stderr, "%s: cannot move %s to %s: %s\n",
150433d6423SLionel Sambuc argv[0], pastjob, job, strerror(errno));
151433d6423SLionel Sambuc unlink(pastjob);
152433d6423SLionel Sambuc exit(1);
153433d6423SLionel Sambuc }
154433d6423SLionel Sambuc printf("%s: %s created\n", argv[0], job);
155433d6423SLionel Sambuc
156433d6423SLionel Sambuc /* Alert cron to the new situation. */
157433d6423SLionel Sambuc if ((fp= fopen(CRONPID, "r")) != NULL) {
158433d6423SLionel Sambuc unsigned long pid;
159433d6423SLionel Sambuc
160433d6423SLionel Sambuc pid= 0;
161433d6423SLionel Sambuc while ((c= fgetc(fp)) != EOF && c != '\n') {
162433d6423SLionel Sambuc if ((unsigned) (c - '0') >= 10) { pid= 0; break; }
163433d6423SLionel Sambuc pid= 10*pid + (c - '0');
164433d6423SLionel Sambuc if (pid >= 30000) { pid= 0; break; }
165433d6423SLionel Sambuc }
166433d6423SLionel Sambuc if (pid > 1) kill((pid_t) pid, SIGHUP);
167433d6423SLionel Sambuc }
168433d6423SLionel Sambuc return(0);
169433d6423SLionel Sambuc }
170433d6423SLionel Sambuc
171433d6423SLionel Sambuc /*-------------------------------------------------------------------------*
172433d6423SLionel Sambuc * getltim() return((time OK) ? daytime : -1) *
173433d6423SLionel Sambuc *-------------------------------------------------------------------------*/
getltim(char * t)174433d6423SLionel Sambuc int getltim(char *t)
175433d6423SLionel Sambuc {
176433d6423SLionel Sambuc if (t[4] == '\0' && t[3] >= '0' && t[3] <= '9' &&
177433d6423SLionel Sambuc t[2] >= '0' && t[2] <= '5' && t[1] >= '0' && t[1] <= '9' &&
178433d6423SLionel Sambuc (t[0] == '0' || t[0] == '1' || (t[1] <= '3' && t[0] == '2')))
179433d6423SLionel Sambuc return(atoi(t));
180433d6423SLionel Sambuc else
181433d6423SLionel Sambuc return(-1);
182433d6423SLionel Sambuc }
183433d6423SLionel Sambuc
184433d6423SLionel Sambuc /*-------------------------------------------------------------------------*
185433d6423SLionel Sambuc * getlday() return ((date OK) ? yearday : -1) *
186433d6423SLionel Sambuc *-------------------------------------------------------------------------*/
getlday(char * m,char * d)187433d6423SLionel Sambuc int getlday(char *m, char *d)
188433d6423SLionel Sambuc {
189433d6423SLionel Sambuc int i, day, im;
190433d6423SLionel Sambuc static int cumday[] = {0, 0, 31, 60, 91, 121, 152,
191433d6423SLionel Sambuc 182, 213, 244, 274, 305, 335};
192433d6423SLionel Sambuc static struct date {
193433d6423SLionel Sambuc char *mon;
194433d6423SLionel Sambuc int dcnt;
195433d6423SLionel Sambuc } *pc, kal[] = {
196433d6423SLionel Sambuc { "Jan", 31 }, { "Feb", 29 }, { "Mar", 31 }, { "Apr", 30 },
197433d6423SLionel Sambuc { "May", 31 }, { "Jun", 30 }, { "Jul", 31 }, { "Aug", 31 },
198433d6423SLionel Sambuc { "Sep", 30 }, { "Oct", 31 }, { "Nov", 30 }, { "Dec", 31 },
199433d6423SLionel Sambuc };
200433d6423SLionel Sambuc
201433d6423SLionel Sambuc pc = kal;
202433d6423SLionel Sambuc im = (digitstring(m)) ? atoi(m) : 0;
203433d6423SLionel Sambuc m[0] &= 0337;
204433d6423SLionel Sambuc for (i = 1; i < 13 && strcmp(m, pc->mon) && im != i; i++, pc++);
205433d6423SLionel Sambuc if (i < 13 && (day = (digitstring(d)) ? atoi(d) : 0) && day <= pc->dcnt) {
206433d6423SLionel Sambuc if (!STARTDAY) day--;
207433d6423SLionel Sambuc return(day + cumday[i]);
208433d6423SLionel Sambuc } else
209433d6423SLionel Sambuc return(-1);
210433d6423SLionel Sambuc }
211433d6423SLionel Sambuc
212433d6423SLionel Sambuc
213433d6423SLionel Sambuc
digitstring(char * s)214433d6423SLionel Sambuc int digitstring(char *s)
215433d6423SLionel Sambuc {
216433d6423SLionel Sambuc while (*s >= '0' && *s <= '9') s++;
217433d6423SLionel Sambuc return((*s == '\0') ? 1 : 0);
218433d6423SLionel Sambuc }
219