1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9#include "monetdb_config.h"
10#include <signal.h>
11#include <unistd.h> /* isatty */
12#include <time.h> /* time, localtime */
13#include <string.h> /* str* */
14#include <sys/types.h> /* open */
15#include <sys/wait.h> /* wait */
16#include <sys/stat.h> /* open */
17#include <fcntl.h> /* open */
18
19#include "utils/properties.h"
20
21#include "merovingian.h"
22#include "handlers.h"
23
24#ifndef O_CLOEXEC
25#define O_CLOEXEC 0
26#endif
27
28
29static const char *sigint = "SIGINT";
30static const char *sigterm = "SIGTERM";
31static const char *sigquit = "SIGQUIT";
32static const char *sighup = "SIGHUP";
33static const char *sigabrt = "SIGABRT";
34static const char *sigsegv = "SIGSEGV";
35static const char *sigbus = "SIGBUS";
36static const char *sigkill = "SIGKILL";
37static const char *
38sigtostr(int sig)
39{
40 switch (sig) {
41 case SIGINT:
42 return(sigint);
43 case SIGTERM:
44 return(sigterm);
45 case SIGQUIT:
46 return(sigquit);
47 case SIGHUP:
48 return(sighup);
49 case SIGABRT:
50 return(sigabrt);
51 case SIGSEGV:
52 return(sigsegv);
53#ifdef SIGBUS
54 case SIGBUS:
55 return(sigbus);
56#endif
57 case SIGKILL:
58 return(sigkill);
59 default:
60 return(NULL);
61 }
62}
63
64/**
65 * Handler for SIGINT, SIGTERM and SIGQUIT. This starts a graceful
66 * shutdown of merovingian.
67 */
68void
69handler(int sig)
70{
71 const char *signame = sigtostr(sig);
72
73 if (write(1, "caught ", 7) < 0 ||
74 (signame ? write(1, signame, strlen(signame)) : write(1, "some signal", 11)) < 0 ||
75 write(1, ", starting shutdown sequence\n", 29) < 0)
76 perror("write failed");
77 _mero_keep_listening = 0;
78}
79
80/* we're not using a lock for setting, reading and clearing this flag
81 * (deadlock!), but we should use atomic instructions */
82static volatile sig_atomic_t hupflag = 0;
83
84/**
85 * Handler for SIGHUP, causes a re-read of the .merovingian_properties
86 * file and the logfile to be reopened.
87 */
88void
89huphandler(int sig)
90{
91 (void) sig;
92
93 hupflag = 1;
94}
95
96void reinitialize(void)
97{
98 int t;
99 time_t now;
100 struct tm *tmp;
101 char mytime[20];
102 char *f;
103 confkeyval *kv;
104
105 if (!hupflag)
106 return;
107
108 hupflag = 0;
109
110 now = time(NULL);
111 tmp = localtime(&now);
112
113 /* re-read properties, we're in our dbfarm */
114 readProps(_mero_props, ".");
115
116 /* check and trim the hash-algo from the passphrase for easy use
117 * lateron */
118 kv = findConfKey(_mero_props, "passphrase");
119 if (kv->val != NULL) {
120 char *h = kv->val + 1;
121 if ((f = strchr(h, '}')) == NULL) {
122 Mfprintf(stderr, "ignoring invalid passphrase: %s\n", kv->val);
123 setConfVal(kv, NULL);
124 } else {
125 *f++ = '\0';
126 if (strcmp(h, MONETDB5_PASSWDHASH) != 0) {
127 Mfprintf(stderr, "ignoring passphrase with incompatible "
128 "password hash: %s\n", h);
129 setConfVal(kv, NULL);
130 } else {
131 setConfVal(kv, f);
132 }
133 }
134 }
135
136 /* have to make sure the logger is not logging anything */
137 pthread_mutex_lock(&_mero_topdp_lock);
138
139 strftime(mytime, sizeof(mytime), "%Y-%m-%d %H:%M:%S", tmp);
140
141 f = getConfVal(_mero_props, "logfile");
142 /* reopen (or open new) file */
143 t = open(f, O_WRONLY | O_APPEND | O_CREAT | O_CLOEXEC, S_IRUSR | S_IWUSR);
144 if (t == -1) {
145 Mfprintf(stderr, "forced to ignore SIGHUP: unable to open "
146 "'%s': %s\n", f, strerror(errno));
147 } else {
148#if O_CLOEXEC == 0
149 (void) fcntl(t, F_SETFD, FD_CLOEXEC);
150#endif
151 Mfprintf(_mero_logfile, "%s END merovingian[%lld]: "
152 "caught SIGHUP, closing logfile\n",
153 mytime, (long long int)_mero_topdp->next->pid);
154 fflush(_mero_logfile);
155 _mero_topdp->out = _mero_topdp->err = t;
156 _mero_logfile = fdopen(t, "a");
157 Mfprintf(_mero_logfile, "%s BEG merovingian[%lld]: "
158 "reopening logfile\n",
159 mytime, (long long int)_mero_topdp->next->pid);
160 }
161
162 /* logger go ahead! */
163 pthread_mutex_unlock(&_mero_topdp_lock);
164}
165
166/**
167 * Wait for and deal with any children that may have exited. This
168 * handler deals with terminated children, by deregistering them from
169 * the internal administration (_mero_topdp) with the necessary
170 * cleanup.
171 */
172void
173childhandler(void)
174{
175 dpair p, q;
176 pid_t pid;
177 int wstatus;
178
179 while ((pid = waitpid(-1, &wstatus, WNOHANG)) > 0) {
180 pthread_mutex_lock(&_mero_topdp_lock);
181
182 /* get the pid from the former child, and locate it in our list */
183 q = _mero_topdp->next;
184 p = q->next;
185 while (p != NULL) {
186 if (p->pid == pid) {
187 /* log everything that's still in the pipes */
188 logFD(p->out, "MSG", p->dbname, (long long int)p->pid, _mero_logfile, 1);
189 /* remove from the list */
190 q->next = p->next;
191 /* close the descriptors */
192 close(p->out);
193 close(p->err);
194 if (WIFEXITED(wstatus)) {
195 Mfprintf(stdout, "database '%s' (%lld) has exited with "
196 "exit status %d\n", p->dbname,
197 (long long int)p->pid, WEXITSTATUS(wstatus));
198 } else if (WIFSIGNALED(wstatus)) {
199 if (WCOREDUMP(wstatus)) {
200 Mfprintf(stdout, "database '%s' (%lld) has crashed "
201 "(dumped core)\n", p->dbname,
202 (long long int)p->pid);
203 } else {
204 const char *sigstr = sigtostr(WTERMSIG(wstatus));
205 char signum[8];
206 if (sigstr == NULL) {
207 snprintf(signum, 8, "%d", WTERMSIG(wstatus));
208 sigstr = signum;
209 }
210 Mfprintf(stdout, "database '%s' (%lld) was killed by signal "
211 "%s\n", p->dbname,
212 (long long int)p->pid, sigstr);
213 }
214 }
215 if (p->dbname)
216 free(p->dbname);
217 free(p);
218 break;
219 }
220 q = p;
221 p = q->next;
222 }
223
224 pthread_mutex_unlock(&_mero_topdp_lock);
225 }
226}
227
228/**
229 * Last resort handler to give a message in the log.
230 */
231void
232segvhandler(int sig) {
233 struct sigaction sa;
234
235 (void)sig;
236
237 /* (try to) ignore any further segfaults */
238 (void) sigemptyset(&sa.sa_mask);
239 sa.sa_flags = 0;
240 sa.sa_handler = SIG_IGN;
241 sigaction(SIGSEGV, &sa, NULL);
242
243 if (_mero_topdp != NULL) {
244 char errmsg[] = "\nSEGMENTATION FAULT OCCURRED\n"
245 "\nA fatal error has occurred which prevents monetdbd from operating."
246 "\nThis is likely a bug in monetdbd, please report it on http://bugs.monetdb.org/"
247 "\nand include the tail of this log in your bugreport with your explanation of "
248 "\nwhat you were doing, if possible.\n"
249 "\nABORTING NOW, YOU HAVE TO MANUALLY KILL ALL REMAINING mserver5 PROCESSES\n";
250 if (write(_mero_topdp->err, errmsg, sizeof(errmsg) - 1) >= 0)
251 sync();
252 }
253 abort();
254}
255
256/* vim:set ts=4 sw=4 noexpandtab: */
257