267 lines
7.7 KiB
C
267 lines
7.7 KiB
C
/*
|
|
* FCRON - periodic command scheduler
|
|
*
|
|
* Copyright 2000-2014 Thibault Godouet <fcron@free.fr>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* The GNU General Public License can also be found in the file
|
|
* `LICENSE' that comes with the fcron source distribution.
|
|
*/
|
|
|
|
|
|
#include "fcronsighup.h"
|
|
#include "global.h"
|
|
#include "allow.h"
|
|
#include "fcronconf.h"
|
|
|
|
|
|
void usage(void);
|
|
void sig_daemon(void);
|
|
pid_t read_pid(void);
|
|
|
|
uid_t uid = 0;
|
|
uid_t fcrontab_uid = 0;
|
|
uid_t rootuid = 0;
|
|
gid_t rootgid = 0;
|
|
|
|
/* needed by log part : */
|
|
char *prog_name = NULL;
|
|
char foreground = 1;
|
|
pid_t daemon_pid = 0;
|
|
|
|
|
|
void
|
|
usage(void)
|
|
/* print a help message about command line options and exit */
|
|
{
|
|
fprintf(stderr,
|
|
"fcronsighup " VERSION_QUOTED " - make fcron update its fcrontabs\n"
|
|
"Copyright " COPYRIGHT_QUOTED " Thibault Godouet <fcron@free.fr>\n"
|
|
"This program is free software distributed WITHOUT ANY WARRANTY.\n"
|
|
"See the GNU General Public License for more details.\n" "\n");
|
|
|
|
fprintf(stderr,
|
|
"fcronsighup [fcronconf]\n"
|
|
" Signal fcron process using fcronconf configuration file\n"
|
|
" (or default configuration file " ETC "/" FCRON_CONF ").\n" "\n");
|
|
|
|
exit(EXIT_ERR);
|
|
|
|
}
|
|
|
|
pid_t
|
|
read_pid(void)
|
|
/* return fcron daemon's pid if running.
|
|
* otherwise return 0 */
|
|
{
|
|
FILE *fp = NULL;
|
|
pid_t pid = 0;
|
|
|
|
if ((fp = fopen(pidfile, "r")) != NULL) {
|
|
if (fscanf(fp, "%" ATTR_SIZE_PIDT "d", CAST_PIDT_PTR & pid) < 1)
|
|
error("Unable to read fcron daemon's pid (fscanf(fp,...))");
|
|
xfclose_check(&fp, pidfile);
|
|
}
|
|
|
|
return pid;
|
|
}
|
|
|
|
|
|
void
|
|
sig_daemon(void)
|
|
/* send SIGHUP to daemon to tell him configuration has changed */
|
|
/* SIGHUP is sent once 10s before the next minute to avoid
|
|
* some bad users to block daemon by sending it SIGHUP all the time */
|
|
{
|
|
/* we don't need to make root wait */
|
|
if (uid != rootuid) {
|
|
time_t t = 0;
|
|
int sl = 0;
|
|
FILE *fp = NULL;
|
|
int fd = 0;
|
|
struct tm *tm = NULL;
|
|
char sigfile[PATH_LEN];
|
|
char buf[PATH_LEN];
|
|
|
|
sigfile[0] = '\0';
|
|
t = time(NULL);
|
|
tm = localtime(&t);
|
|
|
|
if ((sl = 60 - (t % 60) - 10) < 0) {
|
|
if ((tm->tm_min = tm->tm_min + 2) >= 60) {
|
|
tm->tm_hour++;
|
|
tm->tm_min -= 60;
|
|
}
|
|
snprintf(buf, sizeof(buf), "%02d:%02d", tm->tm_hour, tm->tm_min);
|
|
sl = 60 - (t % 60) + 50;
|
|
}
|
|
else {
|
|
if (++tm->tm_min >= 60) {
|
|
tm->tm_hour++;
|
|
tm->tm_min -= 60;
|
|
}
|
|
snprintf(buf, sizeof(buf), "%02d:%02d", tm->tm_hour, tm->tm_min);
|
|
}
|
|
fprintf(stderr, "Modifications will be taken into account"
|
|
" at %s.\n", buf);
|
|
|
|
/* if fcrontabs is too long, snprintf will not be able to add "/fcrontab.sig"
|
|
* string at the end of sigfile */
|
|
if (strlen(fcrontabs) > (sizeof(sigfile) - sizeof("/fcrontab.sig")))
|
|
die("fcrontabs string too long (more than %d characters)",
|
|
(sizeof(sigfile) - sizeof("/fcrontab.sig")));
|
|
snprintf(sigfile, sizeof(sigfile), "%s/fcrontab.sig", fcrontabs);
|
|
|
|
switch (fork()) {
|
|
case -1:
|
|
remove(sigfile);
|
|
die_e("could not fork : daemon has not been signaled");
|
|
break;
|
|
case 0:
|
|
/* child */
|
|
break;
|
|
default:
|
|
/* parent */
|
|
return;
|
|
}
|
|
|
|
foreground = 0;
|
|
|
|
/* try to create a lock file */
|
|
/* // */
|
|
debug("uid: %d, euid: %d, gid: %d, egid: %d", getuid(), geteuid(),
|
|
getgid(), getegid());
|
|
/* // */
|
|
fd = open(sigfile, O_RDWR | O_CREAT, 0644);
|
|
if (fd == -1)
|
|
die_e("can't open or create %s", sigfile);
|
|
fp = fdopen(fd, "r+");
|
|
if (fp == NULL)
|
|
die_e("can't fdopen %s", sigfile);
|
|
|
|
|
|
#ifdef HAVE_FLOCK
|
|
if (flock(fd, LOCK_EX | LOCK_NB) != 0) {
|
|
debug("fcrontab is already waiting for signalling the daemon :"
|
|
" exiting.");
|
|
return;
|
|
}
|
|
#else /* HAVE_FLOCK */
|
|
if (lockf(fd, F_TLOCK, 0) != 0) {
|
|
debug("fcrontab is already waiting for signalling the daemon :"
|
|
" exiting.");
|
|
return;
|
|
}
|
|
#endif /* ! HAVE_FLOCK */
|
|
|
|
sleep(sl);
|
|
|
|
/* also closes the underlying file descriptor fd: */
|
|
xfclose_check(&fp, sigfile);
|
|
/* also reset fd, now closed by xfclose_check(), to make it clear it is closed */
|
|
fd = -1;
|
|
|
|
if (remove(sigfile) < 0)
|
|
error_e("Could not remove %s");
|
|
}
|
|
else
|
|
/* we are root */
|
|
fprintf(stderr,
|
|
"Modifications will be taken into account" " right now.\n");
|
|
|
|
if ((daemon_pid = read_pid()) == 0)
|
|
/* daemon is not running any longer : we exit */
|
|
return;
|
|
|
|
foreground = 1;
|
|
|
|
#ifdef USE_SETE_ID
|
|
if (seteuid(rootuid) != 0)
|
|
error_e("seteuid(rootuid)");
|
|
#endif /* USE_SETE_ID */
|
|
|
|
if (kill(daemon_pid, SIGHUP) != 0)
|
|
die_e("could not send SIGHUP to daemon (pid %d)", daemon_pid);
|
|
|
|
#ifdef USE_SETE_ID
|
|
/* get user's permissions */
|
|
if (seteuid(fcrontab_uid) != 0)
|
|
die_e("Could not change euid to " USERNAME "[%d]", uid);
|
|
#endif /* USE_SETE_ID */
|
|
|
|
}
|
|
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
struct passwd *pass = NULL;
|
|
char *cur_user = NULL;
|
|
|
|
rootuid = get_user_uid_safe(ROOTNAME);
|
|
rootgid = get_group_gid_safe(ROOTGROUP);
|
|
|
|
if (strrchr(argv[0], '/') == NULL)
|
|
prog_name = argv[0];
|
|
else
|
|
prog_name = strrchr(argv[0], '/') + 1;
|
|
|
|
fcrontab_uid = get_user_uid_safe(USERNAME);
|
|
|
|
#ifdef USE_SETE_ID
|
|
/* get user's permissions */
|
|
if (seteuid(fcrontab_uid) != 0)
|
|
die_e("Could not change euid to " USERNAME "[%d]", uid);
|
|
#endif /* USE_SETE_ID */
|
|
|
|
if (argc == 2)
|
|
fcronconf = argv[1];
|
|
else if (argc > 2)
|
|
usage();
|
|
|
|
/* read fcron.conf and update global parameters */
|
|
/* We deactivate output to console, because otherwise it may be used
|
|
* by a malicious user to read some data it is not allow to read
|
|
* (fcronsighup is suid root) */
|
|
foreground = 0;
|
|
read_conf();
|
|
foreground = 1;
|
|
|
|
uid = getuid();
|
|
|
|
/* check if user is allowed to use this program */
|
|
if (!(pass = getpwuid(uid)))
|
|
die("user \"%s\" is not in passwd file. Aborting.", USERNAME);
|
|
cur_user = strdup2(pass->pw_name);
|
|
|
|
if (is_allowed(cur_user)) {
|
|
/* check if daemon is running */
|
|
if ((daemon_pid = read_pid()) != 0)
|
|
sig_daemon();
|
|
else
|
|
fprintf(stderr, "fcron is not running :\n modifications will"
|
|
" be taken into account at its next execution.\n");
|
|
}
|
|
else
|
|
die("User \"%s\" is not allowed to use %s. Aborting.", cur_user,
|
|
prog_name);
|
|
|
|
if (cur_user)
|
|
free(cur_user);
|
|
return EXIT_OK;
|
|
|
|
}
|