4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
26 /* All Rights Reserved */
29 #include <sys/types.h>
31 #include <sys/types.h>
46 #include <security/pam_appl.h>
49 #include "getresponse.h"
53 #define TMPFILE "_cron" /* prefix for tmp file */
54 #define CRMODE 0600 /* mode for creating crontabs */
57 "can't create your crontab file in the crontab directory."
58 #define BADOPEN "can't open your crontab file."
60 "because your login shell isn't /usr/bin/sh, you can't use cron."
61 #define WARNSHELL "warning: commands will be executed using /usr/bin/sh\n"
64 "\tcrontab [file]\n" \
65 "\tcrontab -e [username]\n" \
66 "\tcrontab -l [username]\n" \
67 "\tcrontab -r [username]"
68 #define INVALIDUSER "you are not a valid user (no entry in /etc/passwd)."
69 #define NOTALLOWED "you are not authorized to use cron. Sorry."
71 "you must be super-user to access another user's crontab file"
72 #define EOLN "unexpected end of line."
73 #define UNEXPECT "unexpected character found in line."
74 #define OUTOFBOUND "number out of bounds."
75 #define ERRSFND "errors detected in input, no crontab file generated."
77 " The editor indicates that an error occurred while you were\n"\
78 " editing the crontab data - usually a minor typing error.\n\n"
79 #define BADREAD "error reading your crontab file"
81 " Edit again, to ensure crontab information is intact? "
82 #define NAMETOOLONG "login name too long"
83 #define BAD_SHELL "Invalid shell specified: %s"
84 #define BAD_HOME "Unable to access directory: %s\t%s\n"
93 char line
[CTLINESIZE
];
94 static char login
[UNAMESIZE
];
96 static int next_field(int, int);
97 static void catch(int);
98 static void crabort(char *);
99 static void cerror(char *);
100 static void copycron(FILE *);
103 main(int argc
, char **argv
)
120 char real_login
[UNAMESIZE
];
127 (void) setlocale(LC_ALL
, "");
128 #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
129 #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */
131 (void) textdomain(TEXT_DOMAIN
);
133 if (init_yes() < 0) {
134 (void) fprintf(stderr
, gettext(ERR_MSG_INIT_YES
),
139 while ((c
= getopt(argc
, argv
, "elr")) != EOF
)
155 if (eflag
+ lflag
+ rflag
> 1)
160 if (errflg
|| argc
> 1)
164 if ((pwp
= getpwuid(ruid
)) == NULL
)
165 crabort(INVALIDUSER
);
167 if (strlcpy(real_login
, pwp
->pw_name
, sizeof (real_login
))
168 >= sizeof (real_login
))
169 crabort(NAMETOOLONG
);
171 if ((eflag
|| lflag
|| rflag
) && argc
== 1) {
172 if ((pwp
= getpwnam(*argv
)) == NULL
)
173 crabort(INVALIDUSER
);
175 if (!cron_admin(real_login
)) {
176 if (pwp
->pw_uid
!= ruid
)
190 crabort(INVALIDUSER
);
192 if (strlcpy(login
, pp
, sizeof (login
)) >= sizeof (login
))
193 crabort(NAMETOOLONG
);
194 if (!allowed(login
, CRONALLOW
, CRONDENY
))
197 /* Do account validation check */
198 pam_error
= pam_start("cron", pp
, NULL
, &pamh
);
199 if (pam_error
!= PAM_SUCCESS
) {
200 crabort((char *)pam_strerror(pamh
, pam_error
));
202 pam_error
= pam_acct_mgmt(pamh
, PAM_SILENT
);
203 if (pam_error
!= PAM_SUCCESS
) {
204 (void) fprintf(stderr
, gettext("Warning - Invalid account: "
205 "'%s' not allowed to execute cronjobs\n"), pp
);
207 (void) pam_end(pamh
, PAM_SUCCESS
);
210 cf
= xmalloc(strlen(CRONDIR
)+strlen(login
)+2);
211 strcat(strcat(strcpy(cf
, CRONDIR
), "/"), login
);
215 cron_sendmsg(DELETE
, login
, login
, CRON
);
219 if ((fp
= fopen(cf
, "r")) == NULL
)
221 while (fgets(line
, CTLINESIZE
, fp
) != NULL
)
227 if ((fp
= fopen(cf
, "r")) == NULL
) {
231 (void) strcpy(edtemp
, "/tmp/crontabXXXXXX");
232 tmpfd
= mkstemp(edtemp
);
233 if (fchown(tmpfd
, ruid
, -1) == -1) {
235 crabort("fchown of temporary file failed");
239 * Fork off a child with user's permissions,
240 * to edit the crontab file
242 if ((pid
= fork()) == (pid_t
)-1)
243 crabort("fork failed");
244 if (pid
== 0) { /* child process */
245 /* give up super-user privileges. */
247 if ((tmpfp
= fopen(edtemp
, "w")) == NULL
)
248 crabort("can't create temporary file");
251 * Copy user's crontab file to temporary file.
253 while (fgets(line
, CTLINESIZE
, fp
) != NULL
) {
258 crabort("write error on"
269 if (fclose(tmpfp
) == EOF
)
270 crabort("write error on temporary file");
271 if (stat(edtemp
, &stbuf
) < 0)
272 crabort("can't stat temporary file");
273 omodtime
= stbuf
.st_mtime
;
274 editor
= getenv("EDITOR");
277 buflen
= strlen(editor
) + strlen(edtemp
) + 2;
278 buf
= xmalloc(buflen
);
279 (void) snprintf(buf
, buflen
, "%s %s", editor
, edtemp
);
287 if ((tmpfp
= fopen(edtemp
, "r")) == NULL
)
288 crabort("can't open temporary file");
289 if (fstat(fileno(tmpfp
), &stbuf
) < 0)
290 crabort("can't stat temporary file");
291 if (stbuf
.st_size
== 0)
292 crabort("temporary file empty");
293 if (omodtime
== stbuf
.st_mtime
) {
294 (void) unlink(edtemp
);
295 fprintf(stderr
, gettext(
296 "The crontab file was not"
300 if ((ret
) && (errno
!= EINTR
)) {
302 * Some editors (like 'vi') can return
303 * a non-zero exit status even though
304 * everything is okay. Need to check.
306 fprintf(stderr
, gettext(ED_ERROR
));
308 if (isatty(fileno(stdin
))) {
319 (void) unlink(edtemp
);
324 * Non-interactive, dump changes
326 (void) unlink(edtemp
);
334 /* fix for 1125555 - ignore common signals while waiting */
335 (void) signal(SIGINT
, SIG_IGN
);
336 (void) signal(SIGHUP
, SIG_IGN
);
337 (void) signal(SIGQUIT
, SIG_IGN
);
338 (void) signal(SIGTERM
, SIG_IGN
);
340 if ((stat_loc
& 0xFF00) != 0)
344 * unlink edtemp as 'ruid'. The file contents will be held
345 * since we open the file descriptor 'tmpfp' before calling
348 if (((ret
= seteuid(ruid
)) < 0) ||
349 ((tmpfp
= fopen(edtemp
, "r")) == NULL
) ||
350 (unlink(edtemp
) == -1)) {
351 fprintf(stderr
, "crontab: %s: %s\n",
352 edtemp
, errmsg(errno
));
353 if ((ret
< 0) || (tmpfp
== NULL
))
354 (void) unlink(edtemp
);
363 else if (seteuid(getuid()) != 0 || (fp
= fopen(argv
[0], "r"))
371 cron_sendmsg(ADD
, login
, login
, CRON
);
373 * if (per_errno == 2)
374 * fprintf(stderr, gettext(WARNSHELL));
384 char pid
[6], *tnam_end
;
388 sprintf(pid
, "%-5d", getpid());
389 tnam
= xmalloc(strlen(CRONDIR
)+strlen(TMPFILE
)+7);
390 strcat(strcat(strcat(strcpy(tnam
, CRONDIR
), "/"), TMPFILE
), pid
);
391 /* cut trailing blanks */
392 tnam_end
= strchr(tnam
, ' ');
393 if (tnam_end
!= NULL
)
395 /* catch SIGINT, SIGHUP, SIGQUIT signals */
396 if (signal(SIGINT
, catch) == SIG_IGN
)
397 signal(SIGINT
, SIG_IGN
);
398 if (signal(SIGHUP
, catch) == SIG_IGN
) signal(SIGHUP
, SIG_IGN
);
399 if (signal(SIGQUIT
, catch) == SIG_IGN
) signal(SIGQUIT
, SIG_IGN
);
400 if (signal(SIGTERM
, catch) == SIG_IGN
) signal(SIGTERM
, SIG_IGN
);
401 if ((t
= creat(tnam
, CRMODE
)) == -1) crabort(BADCREATE
);
402 if ((tfp
= fdopen(t
, "w")) == NULL
) {
406 err
= 0; /* if errors found, err set to 1 */
407 while (fgets(line
, CTLINESIZE
, fp
) != NULL
) {
409 while (line
[cursor
] == ' ' || line
[cursor
] == '\t')
411 /* fix for 1039689 - treat blank line like a comment */
412 if (line
[cursor
] == '#' || line
[cursor
] == '\n')
415 if (strncmp(&line
[cursor
], ENV_TZ
, strlen(ENV_TZ
)) == 0) {
418 strncpy(buf
, &line
[cursor
+ strlen(ENV_TZ
)],
420 if ((x
= strchr(buf
, '\n')) != NULL
)
424 } else if (strncmp(&line
[cursor
], ENV_SHELL
,
425 strlen(ENV_SHELL
)) == 0) {
428 strncpy(buf
, &line
[cursor
+ strlen(ENV_SHELL
)],
430 if ((x
= strchr(buf
, '\n')) != NULL
)
433 if (isvalid_shell(buf
)) {
437 fprintf(stderr
, BAD_SHELL
, &line
[cursor
]);
440 } else if (strncmp(&line
[cursor
], ENV_HOME
,
441 strlen(ENV_HOME
)) == 0) {
444 strncpy(buf
, &line
[cursor
+ strlen(ENV_HOME
)],
446 if ((x
= strchr(buf
, '\n')) != NULL
)
448 if (chdir(buf
) == 0) {
452 fprintf(stderr
, BAD_HOME
, &line
[cursor
],
458 if (next_field(0, 59)) continue;
459 if (next_field(0, 23)) continue;
460 if (next_field(1, 31)) continue;
461 if (next_field(1, 12)) continue;
462 if (next_field(0, 06)) continue;
463 if (line
[++cursor
] == '\0') {
468 if (fputs(line
, tfp
) == EOF
) {
477 /* make file tfp the new crontab */
479 if (link(tnam
, cf
) == -1) {
490 next_field(lower
, upper
)
495 while ((line
[cursor
] == ' ') || (line
[cursor
] == '\t')) cursor
++;
496 if (line
[cursor
] == '\0') {
500 if (line
[cursor
] == '*') {
502 if ((line
[cursor
] != ' ') && (line
[cursor
] != '\t')) {
509 if (!isdigit(line
[cursor
])) {
515 num
= num
*10 + (line
[cursor
]-'0');
516 } while (isdigit(line
[++cursor
]));
517 if ((num
< lower
) || (num
> upper
)) {
521 if (line
[cursor
] == '-') {
522 if (!isdigit(line
[++cursor
])) {
528 num2
= num2
*10 + (line
[cursor
]-'0');
529 } while (isdigit(line
[++cursor
]));
530 if ((num2
< lower
) || (num2
> upper
)) {
535 if ((line
[cursor
] == ' ') || (line
[cursor
] == '\t')) break;
536 if (line
[cursor
] == '\0') {
540 if (line
[cursor
++] != ',') {
552 fprintf(stderr
, gettext("%scrontab: error on previous line; %s\n"),
571 if (strcmp(edtemp
, "") != 0) {
573 (void) unlink(edtemp
);
581 fprintf(stderr
, "crontab: %s\n", gettext(msg
));