SIP Witch 1.9.15
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
psignals.cpp
Go to the documentation of this file.
1 // Copyright (C) 2010-2014 David Sugar, Tycho Softworks.
2 // Copyright (C) 2015 Cherokees of Idaho.
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 #include "server.h"
18 
19 #ifdef HAVE_SYS_INOTIFY_H
20 #include <sys/inotify.h>
21 #include <sys/poll.h>
22 #endif
23 
24 namespace sipwitch {
25 
26 #ifdef HAVE_SIGWAIT
27 
28 psignals psignals::thread;
29 
30 psignals::psignals() :
32 {
33  shutdown = started = false;
34 }
35 
36 psignals::~psignals()
37 {
38  if(!shutdown)
39  cancel();
40 }
41 
42 void psignals::cancel(void)
43 {
44  if(started) {
45  shutdown = true;
46 #ifdef __FreeBSD__
47  raise(SIGINT);
48 #endif
49  pthread_kill(tid, SIGALRM);
50  join();
51  }
52 }
53 
54 void psignals::run(void)
55 {
56  int signo;
57  unsigned period = 900;
58 
59  started = true;
60  shell::log(DEBUG1, "starting signal handler");
61 
62  for(;;) {
63  alarm(period);
64 #ifdef HAVE_SIGWAIT2
65  int result = sigwait(&sigs, &signo);
66  if(result) {
67  alarm(0);
68  shell::log(shell::ERR, "signal handler error %d", errno);
69  Thread::sleep(1000);
70  continue;
71  }
72 #else
73  signo = sigwait(&sigs);
74  if(signo < 0) {
75  alarm(0);
76  shell::log(shell::ERR, "signal handler error %d", errno);
77  Thread:sleep(1000);
78  continue;
79  }
80 #endif
81  alarm(0);
82  if(shutdown)
83  break;
84 
85  shell::log(DEBUG1, "received signal %d", signo);
86 
87  switch(signo) {
88  case SIGALRM:
89  shell::log(shell::INFO, "system housekeeping");
90  // registry::cleanup(period);
91  events::sync(period);
92  cache::cleanup();
93  break;
94  case SIGINT:
95  case SIGTERM:
96  control::send("down");
97  break;
98  case SIGUSR1:
99  control::send("snapshot");
100  break;
101  case SIGHUP:
102  control::send("reload");
103  break;
104  default:
105  break;
106  }
107  }
108  shell::log(DEBUG1, "stopping signal handler");
109 }
110 
111 void psignals::service(const char *name)
112 {
113 }
114 
115 void psignals::setup(void)
116 {
117  sigemptyset(&thread.sigs);
118  sigaddset(&thread.sigs, SIGALRM);
119  sigaddset(&thread.sigs, SIGHUP);
120  sigaddset(&thread.sigs, SIGINT);
121  sigaddset(&thread.sigs, SIGTERM);
122  sigaddset(&thread.sigs, SIGUSR1);
123  pthread_sigmask(SIG_BLOCK, &thread.sigs, NULL);
124 
125  signal(SIGPIPE, SIG_IGN);
126 }
127 
128 void psignals::start(void)
129 {
130  thread.background();
131 }
132 
133 void psignals::stop(void)
134 {
135  thread.cancel();
136 }
137 
138 #elif defined(WIN32)
139 
140 static SERVICE_STATUS_HANDLE hStatus = 0;
141 static SERVICE_STATUS status;
142 
143 static void WINAPI handler(DWORD sigint)
144 {
145  switch(sigint) {
146  case 128:
147  // control::request("reload");
148  return;
149  case 129:
150  // control::request("snapshot");
151  case SERVICE_CONTROL_SHUTDOWN:
152  case SERVICE_CONTROL_STOP:
153  status.dwCurrentState = SERVICE_STOP_PENDING;
154  status.dwWin32ExitCode = 0;
155  status.dwCheckPoint = 0;
156  status.dwWaitHint = 6000;
157  SetServiceStatus(hStatus, &status);
158  // control::request("down");
159  break;
160  default:
161  break;
162  }
163 }
164 
165 void psignals::service(const char *name)
166 {
167  memset(&status, 0, sizeof(SERVICE_STATUS));
168  status.dwServiceType = SERVICE_WIN32;
169  status.dwCurrentState = SERVICE_START_PENDING;
170  status.dwControlsAccepted = SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN;
171  hStatus = ::RegisterServiceCtrlHandler(name, &handler);
172 }
173 
174 void psignals::setup(void)
175 {
176 }
177 
178 void psignals::start(void)
179 {
180  if(!hStatus)
181  return;
182 
183  status.dwCurrentState = SERVICE_RUNNING;
184  ::SetServiceStatus(hStatus, &status);
185 }
186 
187 void psignals::stop(void)
188 {
189  if(!hStatus)
190  return;
191 
192  status.dwCurrentState = SERVICE_STOPPED;
193  ::SetServiceStatus(hStatus, &status);
194 }
195 
196 #else
197 
198 void psignals::service(const char *name)
199 {
200 }
201 
202 void psignals::setup(void)
203 {
204 }
205 
206 void psignals::start(void)
207 {
208 }
209 
210 void psignals::stop(void)
211 {
212 }
213 
214 #endif
215 
216 #ifdef HAVE_SYS_INOTIFY_H
217 
218 static const char *dirpath = NULL;
219 static const char *cachepath = NULL;
220 static fd_t watcher = -1;
221 static uint32_t dirnode;
222 static uint32_t cachenode;
223 
224 notify notify::thread;
225 
226 notify::notify() : JoinableThread()
227 {
228 }
229 
230 notify::~notify()
231 {
232  notify::stop();
233 }
234 
235 void notify::start(void)
236 {
237  dirpath = control::env("users");
238  cachepath = control::env("cache");
239 
240  if(!dirpath)
241  dirpath = control::env("prefix");
242 
243  if(fsys::is_dir(dirpath))
244  thread.background();
245  else
246  shell::log(shell::ERR, "notify failed; %s missing", dirpath);
247 }
248 
249 void notify::run(void)
250 {
251  static bool logged = false;
252  timeout_t timeout = -1;
253  unsigned updates = 0;
254  struct pollfd pfd;
255 
256  shell::log(DEBUG1, "notify watching %s", dirpath);
257 
258  watcher = inotify_init();
259  dirnode = inotify_add_watch(watcher, dirpath, IN_CLOSE_WRITE|IN_MOVED_TO|IN_MOVED_FROM|IN_DELETE|IN_DONT_FOLLOW);
260  if(cachepath) {
261  cachenode = inotify_add_watch(watcher, cachepath, IN_CLOSE_WRITE|IN_MOVED_TO|IN_MOVED_FROM|IN_DELETE|IN_DONT_FOLLOW);
262  shell::log(DEBUG1, "notify watching cache %s", cachepath);
263  }
264 
265  while(watcher != -1) {
266  // we want 500 ms of inactivity before actual updating...
267  if(updates)
268  timeout = 500;
269  else
270  timeout = 1000;
271 
272  pfd.fd = watcher;
273  pfd.events = POLLIN | POLLNVAL | POLLERR;
274  pfd.revents = 0;
275  int result = poll(&pfd, 1, timeout);
276  // if error, we sleep....we should also not get errors...
277  if(result < 0) {
278  if(!logged) {
279  shell::log(shell::ERR, "notify error %d", errno);
280  logged = true;
281  }
282  // throttle on repeated errors...
283  Thread::sleep(1000);
284  continue;
285  }
286  if(!result) {
287  if(!updates)
288  continue;
289 
290  // clear updates and process config on timeout
291  control::send("reload");
292  updates = 0;
293  continue;
294  }
295  if(pfd.revents & (POLLNVAL|POLLERR))
296  break;
297 
298  if(pfd.revents & POLLIN) {
299  char buffer[512];
300  size_t offset = 0;
301 
302  size_t len = ::read(watcher, &buffer, sizeof(buffer));
303  if(len < sizeof(struct inotify_event)) {
304  shell::log(shell::ERR, "notify failed to read inotify");
305  continue;
306  }
307 
308  while(offset < len) {
309  struct inotify_event *event = (struct inotify_event *)&buffer[offset];
310  if(!event->len)
311  break;
312 
313  // only if xml files updated do we care...
314  const char *ext = strrchr(event->name, '.');
315  if(ext && eq_case(ext, ".xml")) {
316  shell::log(DEBUG2, "%s updated", event->name);
317  ++updates;
318  }
319  offset += sizeof(struct inotify_event) + event->len;
320  }
321  }
322  }
323 
324  shell::log(DEBUG1, "notify terminating");
325 }
326 
327 void notify::stop(void)
328 {
329  if(watcher != -1) {
330  fd_t fd = watcher;
331  watcher = -1;
332  ::close(fd);
333  thread.join();
334  }
335 }
336 
337 #else
338 
339 void notify::start(void)
340 {
341 }
342 
343 void notify::stop(void)
344 {
345 }
346 
347 #endif
348 
349 } // end namespace
350 
#define DEBUG2
Definition: control.h:50
static bool send(const char *format,...) __PRINTF(1
Send a printf-style message to the control fifo via the file system.
Definition: control.cpp:309
static void stop(void)
Definition: psignals.cpp:343
static void start(void)
Definition: psignals.cpp:339
char * name[96]
Definition: cgiserver.cpp:88
static const char * env(const char *id)
Return the value of a server environment variable.
Definition: control.h:131
#define DEBUG1
Definition: control.h:49