SummaryRefsLogTreeCommitDiffStats
path: root/mcron.c
blob: 7991612f50ca4171fde69b38b50d2b5da368a355 (about) (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/* mcron - run jobs at scheduled times

   Copyright (C) 2015, 2016 Mathieu Lirzin
   Copyright (C) 2003, 2014 Dale Mellor

   This file is part of GNU Mcron.

   GNU Mcron 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 3 of the License, or
   (at your option) any later version.

   GNU Mcron 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 GNU Mcron.  If not, see <http://www.gnu.org/licenses/>.  */

/* This C code represents the thinnest possible wrapper around the Guile code
   which constitutes all the functionality of the mcron program.  There are
   two plus one reasons why we need to do this, and one very unfortunate
   consequence.

   * Firstly, SUID does not work on an executable script.  In the end, it is
     the execution of the translator, in our case guile, which determines the
     effective user, and it is not wise to make the system guile installation
     SUID root!

   * Secondly, executable scripts show up in ugly ways in listings of the
     system process table.  Guile in particular, with its multi-line
     #! ...\ \n -s ...!#
     idiosyncracies shows up in process listings in a way that is difficult
     to determine what program is actually running.

   * A third reason for the C wrapper which might be mentioned is that a
     security-conscious system administrator can choose to only install a
     binary, thus removing the possibility of a user studying a guile script
     and working out ways of hacking it to his own ends, or worse still
     finding a way to modify it to his own ends.

   * Unfortunately, running the guile script from inside a C program means
     that the sigaction function does not work.  Instead, it is necessary to
     perform the signal processing in C.  */

#include <libguile.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>

/* Forward declarations.  */
void inner_main (void *closure, int argc, char **argv);
void react_to_terminal_signal (int sig);
SCM set_cron_signals (void);

int
main (int argc, char **argv)
{
  setenv ("GUILE_LOAD_PATH", GUILE_LOAD_PATH, 1);
  scm_boot_guile (argc, argv, inner_main, 0);

  return EXIT_SUCCESS;
}

/* The effective main function (i.e. the one that actually does some work).
   We register the function above with the guile system, and then execute the
   mcron guile program.  */

void
inner_main (void *closure, int argc, char **argv)
{
  scm_set_current_module (scm_c_resolve_module ("mcron main"));
  scm_c_define_gsubr ("c-set-cron-signals", 0, 0, 0, set_cron_signals);
  scm_c_eval_string ("(main)");
}

/* This is a function designed to be callable from scheme, and sets up all the
   signal handlers required by the cron personality.  */

SCM
set_cron_signals ()
{
  static struct sigaction sa;

  memset (&sa, 0, sizeof (sa));
  sa.sa_handler = react_to_terminal_signal;
  sigaction (SIGTERM, &sa, 0);
  sigaction (SIGINT,  &sa, 0);
  sigaction (SIGQUIT, &sa, 0);
  sigaction (SIGHUP,  &sa, 0);

  return SCM_BOOL_T;
}

/* This is a function designed to be installed as a signal handler, for
   signals which are supposed to initiate shutdown of this program.  It calls
   the scheme procedure (see mcron.scm for details) to do all the work, and
   then exits.  */

void
react_to_terminal_signal (int sig)
{
  scm_c_eval_string ("(delete-run-file)");
  exit (EXIT_FAILURE);
}