cfengine  3.15.4
About: CFEngine is a configuration management system for configuring and maintaining Unix-like computers (using an own high level policy language). Community version.
  Fossies Dox: cfengine-3.15.4.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

signals.c
Go to the documentation of this file.
1 /*
2  Copyright 2019 Northern.tech AS
3 
4  This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6  This program is free software; you can redistribute it and/or modify it
7  under the terms of the GNU General Public License as published by the
8  Free Software Foundation; version 3.
9 
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with this program; if not, write to the Free Software
17  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18 
19  To the extent this program is licensed as part of the Enterprise
20  versions of CFEngine, the applicable Commercial Open Source License
21  (COSL) may apply to this file if you as a licensee so wish it. See
22  included file COSL.txt.
23 */
24 
25 #include <signals.h>
26 #include <cleanup.h>
27 #include <known_dirs.h> /* GetStateDir() */
28 #include <file_lib.h> /* FILE_SEPARATOR */
29 
30 static bool PENDING_TERMINATION = false; /* GLOBAL_X */
31 
32 static bool RELOAD_CONFIG = false; /* GLOBAL_X */
33 /********************************************************************/
34 
36 {
37  return PENDING_TERMINATION;
38 }
39 
41 {
42  return RELOAD_CONFIG;
43 }
44 
46 {
47  RELOAD_CONFIG = false;
48 }
49 
50 /********************************************************************/
51 
52 static int SIGNAL_PIPE[2] = { -1, -1 }; /* GLOBAL_C */
53 
54 static void CloseSignalPipe(void)
55 {
56  int c = 2;
57  while (c > 0)
58  {
59  c--;
60  if (SIGNAL_PIPE[c] >= 0)
61  {
62  close(SIGNAL_PIPE[c]);
63  SIGNAL_PIPE[c] = -1;
64  }
65  }
66 }
67 
68 /**
69  * Make a pipe that can be used to flag that a signal has arrived.
70  * Using a pipe avoids race conditions, since it saves its values until emptied.
71  * Use GetSignalPipe() to get the pipe.
72  * Note that we use a real socket as the pipe, because Windows only supports
73  * using select() with real sockets. This means also using send() and recv()
74  * instead of write() and read().
75  */
76 void MakeSignalPipe(void)
77 {
78  if (socketpair(AF_UNIX, SOCK_STREAM, 0, SIGNAL_PIPE) != 0)
79  {
80  Log(LOG_LEVEL_CRIT, "Could not create internal communication pipe. Cannot continue. (socketpair: '%s')",
81  GetErrorStr());
82  DoCleanupAndExit(EXIT_FAILURE);
83  }
84 
86 
87  for (int c = 0; c < 2; c++)
88  {
89 #ifdef __MINGW32__
90  u_long enable = 1;
91  int ret = ioctlsocket(SIGNAL_PIPE[c], FIONBIO, &enable);
92 #define CNTLNAME "ioctlsocket"
93 #else /* Unix: */
94  int ret = fcntl(SIGNAL_PIPE[c], F_SETFL, O_NONBLOCK);
95 #define CNTLNAME "fcntl"
96 #endif /* __MINGW32__ */
97 
98  if (ret != 0)
99  {
101  "Could not unblock internal communication pipe. "
102  "Cannot continue. (" CNTLNAME ": '%s')",
103  GetErrorStr());
104  DoCleanupAndExit(EXIT_FAILURE);
105  }
106 #undef CNTLNAME
107  }
108 }
109 
110 /**
111  * Gets the signal pipe, which is non-blocking.
112  * Each byte read corresponds to one arrived signal.
113  * Note: Use recv() to read from the pipe, not read().
114  */
115 int GetSignalPipe(void)
116 {
117  return SIGNAL_PIPE[0];
118 }
119 
120 static void SignalNotify(int signum)
121 {
122  unsigned char sig = (unsigned char)signum;
123  if (SIGNAL_PIPE[1] >= 0)
124  {
125  // send() is async-safe, according to POSIX.
126  if (send(SIGNAL_PIPE[1], &sig, 1, 0) < 0)
127  {
128  // These signal contention. Everything else is an error.
129  if (errno != EAGAIN
130 #ifndef __MINGW32__
131  && errno != EWOULDBLOCK
132 #endif
133  )
134  {
135  // This is not async safe, but if we get in here there's something really weird
136  // going on.
137  Log(LOG_LEVEL_CRIT, "Could not write to signal pipe. Unsafe to continue. (write: '%s')",
138  GetErrorStr());
139  _exit(EXIT_FAILURE);
140  }
141  }
142  }
143 }
144 
145 void HandleSignalsForAgent(int signum)
146 {
147  switch (signum)
148  {
149  case SIGTERM:
150  case SIGINT:
151  /* TODO don't exit from the signal handler, just set a flag. Reason is
152  * that all the cleanup() hooks we register are not reentrant. */
153  DoCleanupAndExit(0);
154  case SIGBUS:
155  /* SIGBUS almost certainly means a violation of mmap() area boundaries
156  * or some mis-aligned memory access. IOW, an LMDB corruption. */
157  {
158  char filename[PATH_MAX] = { 0 }; /* trying to avoid memory allocation */
159  xsnprintf(filename, PATH_MAX, "%s%c%s",
161  int fd = open(filename, O_CREAT|O_RDWR, CF_PERMS_DEFAULT);
162  if (fd != -1)
163  {
164  close(fd);
165  }
166 
167  /* avoid calling complex logging functions */
168  fprintf(stdout, "process killed by SIGBUS\n");
169 
170  /* else: we tried, nothing more to do in the limited environment of a
171  * signal handler */
172  _exit(1);
173  }
174  break;
175  case SIGUSR1:
177  break;
178  case SIGUSR2:
180  break;
181  default:
182  /* No action */
183  break;
184  }
185 
186  SignalNotify(signum);
187 
188 /* Reset the signal handler */
189  signal(signum, HandleSignalsForAgent);
190 }
191 
192 /********************************************************************/
193 
194 void HandleSignalsForDaemon(int signum)
195 {
196  switch (signum)
197  {
198  case SIGTERM:
199  case SIGINT:
200  case SIGSEGV:
201  case SIGKILL:
202  PENDING_TERMINATION = true;
203  break;
204  case SIGBUS:
205  /* SIGBUS almost certainly means a violation of mmap() area boundaries
206  * or some mis-aligned memory access. IOW, an LMDB corruption. */
207  {
208  char filename[PATH_MAX] = { 0 }; /* trying to avoid memory allocation */
209  xsnprintf(filename, PATH_MAX, "%s%c%s",
211  int fd = open(filename, O_CREAT|O_RDWR, CF_PERMS_DEFAULT);
212  if (fd != -1)
213  {
214  close(fd);
215  }
216 
217  /* avoid calling complex logging functions */
218  fprintf(stdout, "process killed by SIGBUS\n");
219 
220  /* else: we tried, nothing more to do in the limited environment of a
221  * signal handler */
222  _exit(1);
223  }
224  break;
225  case SIGUSR1:
227  break;
228  case SIGUSR2:
230  break;
231  case SIGHUP:
232  RELOAD_CONFIG = true;
233  break;
234  case SIGPIPE:
235  default:
236  /* No action */
237  break;
238  }
239 
240  /* Notify processes that use the signal pipe (cf-serverd). */
241  SignalNotify(signum);
242 
243  /* Reset the signal handler. */
244  signal(signum, HandleSignalsForDaemon);
245 }
#define CF_DB_REPAIR_TRIGGER
Definition: cf3.defs.h:97
void RegisterCleanupFunction(CleanupFn fn)
Definition: cleanup.c:63
void DoCleanupAndExit(int ret)
Definition: cleanup.c:57
#define CF_PERMS_DEFAULT
Definition: definitions.h:58
#define FILE_SEPARATOR
Definition: file_lib.h:102
int errno
const char * GetStateDir(void)
Definition: known_dirs.c:186
void LogSetGlobalLevel(LogLevel level)
Definition: logging.c:561
const char * GetErrorStr(void)
Definition: logging.c:275
void Log(LogLevel level, const char *fmt,...)
Definition: logging.c:409
@ LOG_LEVEL_NOTICE
Definition: logging.h:44
@ LOG_LEVEL_DEBUG
Definition: logging.h:47
@ LOG_LEVEL_CRIT
Definition: logging.h:41
void xsnprintf(char *str, size_t str_size, const char *format,...)
Definition: misc_lib.c:114
int socketpair(int domain, int type, int protocol, int sv[2])
#define PATH_MAX
Definition: platform.h:176
void HandleSignalsForDaemon(int signum)
Definition: signals.c:194
static bool RELOAD_CONFIG
Definition: signals.c:32
int GetSignalPipe(void)
Definition: signals.c:115
static bool PENDING_TERMINATION
Definition: signals.c:30
void HandleSignalsForAgent(int signum)
Definition: signals.c:145
#define CNTLNAME
static void CloseSignalPipe(void)
Definition: signals.c:54
void ClearRequestReloadConfig()
Definition: signals.c:45
static void SignalNotify(int signum)
Definition: signals.c:120
bool IsPendingTermination(void)
Definition: signals.c:35
bool ReloadConfigRequested(void)
Definition: signals.c:40
static int SIGNAL_PIPE[2]
Definition: signals.c:52
void MakeSignalPipe(void)
Definition: signals.c:76