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 | |
29 | static const char *sigint = "SIGINT" ; |
30 | static const char *sigterm = "SIGTERM" ; |
31 | static const char *sigquit = "SIGQUIT" ; |
32 | static const char *sighup = "SIGHUP" ; |
33 | static const char *sigabrt = "SIGABRT" ; |
34 | static const char *sigsegv = "SIGSEGV" ; |
35 | static const char *sigbus = "SIGBUS" ; |
36 | static const char *sigkill = "SIGKILL" ; |
37 | static const char * |
38 | sigtostr(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 | */ |
68 | void |
69 | handler(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 */ |
82 | static 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 | */ |
88 | void |
89 | huphandler(int sig) |
90 | { |
91 | (void) sig; |
92 | |
93 | hupflag = 1; |
94 | } |
95 | |
96 | void 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 | */ |
172 | void |
173 | childhandler(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 | */ |
231 | void |
232 | segvhandler(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 | |