386BSD 0.1 development
[unix-history] / usr / src / usr.bin / crontab / crontab.c
CommitLineData
33ee458a
WJ
1#if !defined(lint) && !defined(LINT)
2static char rcsid[] = "$Header: /home/local/site/cron2.1/orig/RCS/crontab.c,v 2.3 1992/05/13 05:56:35 rich Exp $";
3#endif
4
5/* Revision 1.5 87/05/02 17:33:22 paul
6 * pokecron? (RCS file has the rest of the log)
7 *
8 * Revision 1.5 87/05/02 17:33:22 paul
9 * baseline for mod.sources release
10 *
11 * Revision 1.4 87/03/31 13:11:48 paul
12 * I won't say that rs@mirror gave me this idea but crontab uses getopt() now
13 *
14 * Revision 1.3 87/03/30 23:43:48 paul
15 * another suggestion from rs@mirror:
16 * use getpwuid(getuid)->pw_name instead of getenv("USER")
17 * this is a boost to security...
18 *
19 * Revision 1.2 87/02/11 17:40:12 paul
20 * changed command syntax to allow append and replace instead of append as
21 * default and no replace at all.
22 *
23 * Revision 1.1 87/01/26 23:49:06 paul
24 * Initial revision
25 */
26
27/* Copyright 1988,1990 by Paul Vixie
28 * All rights reserved
29 *
30 * Distribute freely, except: don't remove my name from the source or
31 * documentation (don't take credit for my work), mark your changes (don't
32 * get me blamed for your possible bugs), don't alter or remove this
33 * notice. May be sold if buildable source is provided to buyer. No
34 * warrantee of any kind, express or implied, is included with this
35 * software; use at your own risk, responsibility for damages (if any) to
36 * anyone resulting from the use of this software rests entirely with the
37 * user.
38 *
39 * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
40 * I'll try to keep a version up to date. I can be reached as follows:
41 * Paul Vixie, 329 Noe Street, San Francisco, CA, 94114, (415) 864-7013,
42 * paul@vixie.sf.ca.us || {hoptoad,pacbell,decwrl,crash}!vixie!paul
43 */
44
45
46#define MAIN_PROGRAM
47
48
49#include "cron.h"
50#include <pwd.h>
51#include <errno.h>
52#include <sys/file.h>
53#if defined(BSD)
54# include <sys/time.h>
55#endif /*BSD*/
56
57/* extern char *sprintf(); */
58
59
60static int Pid;
61static char User[MAX_UNAME], RealUser[MAX_UNAME];
62static char Filename[MAX_FNAME];
63static FILE *NewCrontab;
64static int CheckErrorCount;
65static enum {opt_unknown, opt_list, opt_delete, opt_replace}
66 Option;
67
68extern void log_it();
69
70#if DEBUGGING
71static char *Options[] = {"???", "list", "delete", "replace"};
72#endif
73
74static void
75usage()
76{
77 fprintf(stderr, "usage: %s [-u user] ...\n", ProgramName);
78 fprintf(stderr, " ... -l (list user's crontab)\n");
79 fprintf(stderr, " ... -d (delete user's crontab)\n");
80 fprintf(stderr, " ... -r file (replace user's crontab)\n");
81 exit(ERROR_EXIT);
82}
83
84
85main(argc, argv)
86 int argc;
87 char *argv[];
88{
89 void parse_args(), set_cron_uid(), set_cron_cwd(),
90 list_cmd(), delete_cmd(), replace_cmd();
91
92 Pid = getpid();
93 ProgramName = argv[0];
94#if defined(BSD)
95 setlinebuf(stderr);
96#endif
97 parse_args(argc, argv); /* sets many globals, opens a file */
98 set_cron_uid();
99 set_cron_cwd();
100 if (!allowed(User)) {
101 fprintf(stderr,
102 "You (%s) are not allowed to use this program (%s)\n",
103 User, ProgramName);
104 fprintf(stderr, "See crontab(1) for more information\n");
105 log_it(RealUser, Pid, "AUTH", "crontab command not allowed");
106 exit(ERROR_EXIT);
107 }
108 switch (Option)
109 {
110 case opt_list: list_cmd();
111 break;
112 case opt_delete: delete_cmd();
113 break;
114 case opt_replace: replace_cmd();
115 break;
116 }
117}
118
119
120static void
121parse_args(argc, argv)
122 int argc;
123 char *argv[];
124{
125 void usage();
126 char *getenv(), *strcpy();
127 int getuid();
128 struct passwd *getpwnam();
129 extern int getopt(), optind;
130 extern char *optarg;
131
132 struct passwd *pw;
133 int argch;
134
135 if (!(pw = getpwuid(getuid())))
136 {
137 fprintf(stderr, "%s: your UID isn't in the passwd file.\n",
138 ProgramName);
139 fprintf(stderr, "bailing out.\n");
140 exit(ERROR_EXIT);
141 }
142 strcpy(User, pw->pw_name);
143 strcpy(RealUser, User);
144 Filename[0] = '\0';
145 Option = opt_unknown;
146 while (EOF != (argch = getopt(argc, argv, "u:ldr:x:")))
147 {
148 switch (argch)
149 {
150 case 'x':
151 if (!set_debug_flags(optarg))
152 usage();
153 break;
154 case 'u':
155 if (getuid() != ROOT_UID)
156 {
157 fprintf(stderr,
158 "must be privileged to use -u\n");
159 exit(ERROR_EXIT);
160 }
161 if ((struct passwd *)NULL == getpwnam(optarg))
162 {
163 fprintf(stderr, "%s: user `%s' unknown\n",
164 ProgramName, optarg);
165 exit(ERROR_EXIT);
166 }
167 (void) strcpy(User, optarg);
168 break;
169 case 'l':
170 if (Option != opt_unknown)
171 usage();
172 Option = opt_list;
173 break;
174 case 'd':
175 if (Option != opt_unknown)
176 usage();
177 Option = opt_delete;
178 break;
179 case 'r':
180 if (Option != opt_unknown)
181 usage();
182 Option = opt_replace;
183 (void) strcpy(Filename, optarg);
184 break;
185 default:
186 usage();
187 }
188 }
189
190 endpwent();
191
192 if (Option == opt_unknown || argv[optind] != NULL)
193 usage();
194
195 if (Option == opt_replace) {
196 if (!Filename[0]) {
197 /* getopt(3) says this can't be true
198 * but I'm paranoid today.
199 */
200 fprintf(stderr, "filename must be given for -a or -r\n");
201 usage();
202 }
203 /* we have to open the file here because we're going to
204 * chdir(2) into /var/cron before we get around to
205 * reading the file.
206 */
207 if (!strcmp(Filename, "-")) {
208 NewCrontab = stdin;
209 } else {
210 if (!(NewCrontab = fopen(Filename, "r"))) {
211 perror(Filename);
212 exit(ERROR_EXIT);
213 }
214 }
215 }
216
217 Debug(DMISC, ("user=%s, file=%s, option=%s\n",
218 User, Filename, Options[(int)Option]))
219}
220
221
222static void
223list_cmd()
224{
225 extern errno;
226 char n[MAX_FNAME];
227 FILE *f;
228 int ch;
229
230 log_it(RealUser, Pid, "LIST", User);
231 (void) sprintf(n, CRON_TAB(User));
232 if (!(f = fopen(n, "r")))
233 {
234 if (errno == ENOENT)
235 fprintf(stderr, "no crontab for %s\n", User);
236 else
237 perror(n);
238 exit(ERROR_EXIT);
239 }
240
241 /* file is open. copy to stdout, close.
242 */
243 Set_LineNum(1)
244 while (EOF != (ch = get_char(f)))
245 putchar(ch);
246 fclose(f);
247}
248
249
250static void
251delete_cmd()
252{
253 extern errno;
254 int unlink();
255 void poke_daemon();
256 char n[MAX_FNAME];
257
258 log_it(RealUser, Pid, "DELETE", User);
259 (void) sprintf(n, CRON_TAB(User));
260 if (unlink(n))
261 {
262 if (errno == ENOENT)
263 fprintf(stderr, "no crontab for %s\n", User);
264 else
265 perror(n);
266 exit(ERROR_EXIT);
267 }
268 poke_daemon();
269}
270
271
272static void
273check_error(msg)
274 char *msg;
275{
276 CheckErrorCount += 1;
277 fprintf(stderr, "\"%s\", line %d: %s\n", Filename, LineNumber, msg);
278}
279
280
281static void
282replace_cmd()
283{
284 char *sprintf();
285 entry *load_entry();
286 int load_env();
287 int unlink();
288 void free_entry();
289 void check_error();
290 void poke_daemon();
291 extern errno;
292
293 char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME];
294 FILE *tmp;
295 int ch;
296 entry *e;
297 int status;
298 time_t now = time(NULL);
299
300 (void) sprintf(n, "tmp.%d", Pid);
301 (void) sprintf(tn, CRON_TAB(n));
302 if (!(tmp = fopen(tn, "w"))) {
303 perror(tn);
304 exit(ERROR_EXIT);
305 }
306
307 /* write a signature at the top of the file. for brian.
308 */
309 fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now));
310 fprintf(tmp, "# (Cron version -- %s)\n", rcsid);
311
312 /* copy the crontab to the tmp
313 */
314 Set_LineNum(1)
315 while (EOF != (ch = get_char(NewCrontab)))
316 putc(ch, tmp);
317 fclose(NewCrontab);
318 fflush(tmp); rewind(tmp);
319
320 if (ferror(tmp)) {
321 fprintf("%s: error while writing new crontab to %s\n",
322 ProgramName, tn);
323 fclose(tmp); unlink(tn);
324 exit(ERROR_EXIT);
325 }
326
327 /* check the syntax of the file being installed.
328 */
329
330 /* BUG: was reporting errors after the EOF if there were any errors
331 * in the file proper -- kludged it by stopping after first error.
332 * vix 31mar87
333 */
334 CheckErrorCount = 0;
335 while (!CheckErrorCount && (status = load_env(envstr, tmp)) >= OK)
336 {
337 if (status == FALSE)
338 {
339 if (NULL != (e = load_entry(NewCrontab, check_error)))
340 free((char *) e);
341 }
342 }
343
344 if (CheckErrorCount != 0)
345 {
346 fprintf(stderr, "errors in crontab file, can't install.\n");
347 fclose(tmp); unlink(tn);
348 exit(ERROR_EXIT);
349 }
350
351 if (fchown(fileno(tmp), ROOT_UID, -1) < OK)
352 {
353 perror("chown");
354 fclose(tmp); unlink(tn);
355 exit(ERROR_EXIT);
356 }
357
358 if (fchmod(fileno(tmp), 0600) < OK)
359 {
360 perror("chown");
361 fclose(tmp); unlink(tn);
362 exit(ERROR_EXIT);
363 }
364
365 if (fclose(tmp) == EOF) {
366 perror("fclose");
367 unlink(tn);
368 exit(ERROR_EXIT);
369 }
370
371 (void) sprintf(n, CRON_TAB(User));
372 if (rename(tn, n))
373 {
374 fprintf(stderr, "%s: error renaming %s to %s\n",
375 ProgramName, tn, n);
376 perror("rename");
377 unlink(tn);
378 exit(ERROR_EXIT);
379 }
380 log_it(RealUser, Pid, "REPLACE", User);
381
382 poke_daemon();
383}
384
385
386static void
387poke_daemon()
388{
389#if defined(BSD)
390 struct timeval tvs[2];
391 struct timezone tz;
392
393 (void) gettimeofday(&tvs[0], &tz);
394 tvs[1] = tvs[0];
395 if (utimes(SPOOL_DIR, tvs) < OK)
396 {
397 fprintf(stderr, "crontab: can't update mtime on spooldir\n");
398 perror(SPOOL_DIR);
399 return;
400 }
401#endif /*BSD*/
402#if defined(ATT)
403 if (utime(SPOOL_DIR, NULL) < OK)
404 {
405 fprintf(stderr, "crontab: can't update mtime on spooldir\n");
406 perror(SPOOL_DIR);
407 return;
408 }
409#endif /*ATT*/
410}