SIP Witch 1.9.15
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
control.cpp
Go to the documentation of this file.
1 // Copyright (C) 2006-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 <sipwitch-config.h>
18 #include <ucommon/ucommon.h>
19 #include <ucommon/export.h>
20 #include <sipwitch/control.h>
21 #include <sipwitch/service.h>
22 #include <sipwitch/modules.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 
29 namespace sipwitch {
30 
31 static const char *replytarget = NULL;
32 
33 shell_t *control::args = NULL;
34 
35 #ifndef _MSWINDOWS_
36 
37 #include <fcntl.h>
38 #include <signal.h>
39 #include <syslog.h>
40 #include <sys/time.h>
41 #include <sys/resource.h>
42 #include <sys/wait.h>
43 #include <limits.h>
44 #include <unistd.h>
45 #include <pwd.h>
46 
47 static FILE *fifo = NULL;
48 static char fifopath[128] = "";
49 
50 static void cleanup(void)
51 {
52  if(fifopath[0]) {
53  ::remove(fifopath);
54  char *cp = strrchr(fifopath, '/');
55  String::set(cp, 10, "/pidfile");
56  ::remove(fifopath);
57  fifopath[0] = 0;
58  }
59 }
60 
61 size_t control::attach(shell_t *envp)
62 {
63  args = envp;
64 
65  String::set(fifopath, sizeof(fifopath), env("control"));
66  remove(fifopath);
67  if(mkfifo(fifopath, fsys::GROUP_PRIVATE)) {
68  fifopath[0] = 0;
69  return 0;
70  }
71  else
72  shell::exiting(&cleanup);
73 
74  fifo = fopen(fifopath, "r+");
75  if(fifo)
76  return 512;
77  fifopath[0] = 0;
78  return 0;
79 }
80 
81 void control::release(void)
82 {
83  shell::log(shell::INFO, "shutdown");
84  cleanup();
85 }
86 
87 char *control::receive(void)
88 {
89  static char buf[512];
90  char *cp;
91 
92  if(!fifo)
93  return NULL;
94 
95  reply(NULL);
96 
97 retry:
98  buf[0] = 0;
99  if(fgets(buf, sizeof(buf), fifo) == NULL) {
100  buf[0] = 0;
101  Thread::sleep(100); // throttle if dead...
102  }
103  cp = String::strip(buf, " \t\r\n");
104  if(*cp == '/') {
105  if(strstr(cp, ".."))
106  goto retry;
107 
108  if(strncmp(cp, "/tmp/.reply.", 12))
109  goto retry;
110  }
111 
112  if(*cp == '/' || isdigit(*cp)) {
113  replytarget = cp;
114  while(*cp && !isspace(*cp))
115  ++cp;
116  *(cp++) = 0;
117  while(isspace(*cp))
118  ++cp;
119  }
120  return cp;
121 }
122 
123 #else
124 
125 static HANDLE hFifo = INVALID_HANDLE_VALUE;
126 static HANDLE hLoopback = INVALID_HANDLE_VALUE;
127 static HANDLE hEvent = INVALID_HANDLE_VALUE;
128 static OVERLAPPED ovFifo;
129 
130 static void cleanup(void)
131 {
132  if(hFifo != INVALID_HANDLE_VALUE) {
133  CloseHandle(hFifo);
134  CloseHandle(hLoopback);
135  CloseHandle(hEvent);
136  hFifo = hLoopback = hEvent = INVALID_HANDLE_VALUE;
137  }
138 }
139 
140 size_t control::attach(shell_t *envp)
141 {
142  char buf[64];
143 
144  args = envp;
145 
146  String::set(buf, sizeof(buf), env("control"));
147  hFifo = CreateMailslot(buf, 0, MAILSLOT_WAIT_FOREVER, NULL);
148  if(hFifo == INVALID_HANDLE_VALUE)
149  return 0;
150 
151  hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
152  hLoopback = CreateFile(buf, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
153  ovFifo.Offset = 0;
154  ovFifo.OffsetHigh = 0;
155  ovFifo.hEvent = hEvent;
156  shell::exiting(&cleanup);
157  return 464;
158 }
159 
160 char *control::receive(void)
161 {
162  static char buf[464];
163  BOOL result;
164  DWORD msgresult;
165  const char *lp;
166  char *cp;
167 
168  if(hFifo == INVALID_HANDLE_VALUE)
169  return NULL;
170 
171  reply(NULL);
172 
173 retry:
174  result = ReadFile(hFifo, buf, sizeof(buf) - 1, &msgresult, &ovFifo);
175  if(!result && GetLastError() == ERROR_IO_PENDING) {
176  int ret = WaitForSingleObject(ovFifo.hEvent, INFINITE);
177  if(ret != WAIT_OBJECT_0)
178  return NULL;
179  result = GetOverlappedResult(hFifo, &ovFifo, &msgresult, TRUE);
180  }
181 
182  if(!result || msgresult < 1)
183  return NULL;
184 
185  buf[msgresult] = 0;
186  cp = String::strip(buf, " \t\r\n");
187 
188  if(*cp == '\\') {
189  if(strstr(cp, ".."))
190  goto retry;
191 
192  if(strncmp(cp, "\\\\.\\mailslot\\", 14))
193  goto retry;
194  }
195 
196  if(*cp == '\\' || isdigit(*cp)) {
197  replytarget = cp;
198  while(*cp && !isspace(*cp))
199  ++cp;
200  *(cp++) = 0;
201  while(isspace(*cp))
202  ++cp;
203  lp = replytarget + strlen(replytarget) - 6;
204  if(stricmp(lp, "_temp"))
205  goto retry;
206  }
207  return cp;
208 }
209 
210 void control::release(void)
211 {
212  shell::log(shell::INFO, "shutdown");
213  cleanup();
214 }
215 
216 #endif
217 
218 void control::reply(const char *msg)
219 {
220  assert(msg == NULL || *msg != 0);
221 
222  char *sid;
223  fsys fd;
224  char buffer[256];
225 
226  if(msg)
227  shell::log(shell::ERR, "control failed; %s", msg);
228 
229  if(!replytarget)
230  return;
231 
232  if(isdigit(*replytarget)) {
233 #ifndef _MSWINDOWS_
234  pid_t pid = atoi(replytarget);
235  if(msg)
236  kill(pid, SIGUSR2);
237  else
238  kill(pid, SIGUSR1);
239 #endif
240  }
241  else {
242  sid = (char *)strchr(replytarget, ';');
243  if(sid)
244  *(sid++) = 0;
245 
246  else
247  sid = (char *)"-";
248  if(msg)
249  snprintf(buffer, sizeof(buffer), "%s msg %s\n", sid, msg);
250  else
251  snprintf(buffer, sizeof(buffer), "%s ok\n", sid);
252  fd.open(replytarget, fsys::WRONLY);
253  if(is(fd)) {
254  fd.write(buffer, strlen(buffer));
255  fd.close();
256  }
257  }
258  replytarget = NULL;
259 }
260 
261 bool control::libexec(const char *fmt, ...)
262 {
263  assert(fmt != NULL);
264 
265  va_list vargs;
266  char buf[256];
267 
268  va_start(vargs, fmt);
269  if(fmt)
270  vsnprintf(buf, sizeof(buf), fmt, vargs);
271  va_end(vargs);
272 
273  shell::debug(5, "executing %s", buf);
274 
275 #ifdef _MSWINDOWS_
276 #else
277  int max = sizeof(fd_set) * 8;
278  pid_t pid = fork();
279 #ifdef RLIMIT_NOFILE
280  struct rlimit rlim;
281 
282  if(!getrlimit(RLIMIT_NOFILE, &rlim))
283  max = rlim.rlim_max;
284 #endif
285  if(pid) {
286  waitpid(pid, NULL, 0);
287  return true;
288  }
289  ::signal(SIGABRT, SIG_DFL);
290  ::signal(SIGQUIT, SIG_DFL);
291  ::signal(SIGINT, SIG_DFL);
292  ::signal(SIGCHLD, SIG_DFL);
293  ::signal(SIGPIPE, SIG_DFL);
294  int fd = open("/dev/null", O_RDWR);
295  dup2(fd, 0);
296  dup2(fd, 2);
297  dup2(fileno(fifo), 1);
298  for(fd = 3; fd < max; ++fd)
299  ::close(fd);
300  pid = fork();
301  if(pid > 0)
302  ::exit(0);
303  ::execlp("/bin/sh", "sh", "-c", buf, NULL);
304  ::exit(127);
305 #endif
306  return true;
307 }
308 
309 bool control::send(const char *fmt, ...)
310 {
311  assert(fmt != NULL && *fmt != 0);
312 
313  char buf[512];
314  fd_t fd;
315  int len;
316  bool rtn = true;
317  va_list vargs;
318 
319  va_start(vargs, fmt);
320 #ifdef _MSWINDOWS_
321  fd = CreateFile(env("control"), GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
322  if(fd == INVALID_HANDLE_VALUE) {
323  va_end(vargs);
324  return false;
325  }
326 
327 #else
328  fd = open(env("control"), O_WRONLY | O_NONBLOCK);
329  if(fd < 0) {
330  va_end(vargs);
331  return false;
332  }
333 #endif
334 
335  vsnprintf(buf, sizeof(buf) - 1, fmt, vargs);
336  va_end(vargs);
337  len = strlen(buf);
338  if(buf[len - 1] != '\n')
339  buf[len++] = '\n';
340 #ifdef _MSWINDOWS_
341  if(!WriteFile(fd, buf, (DWORD)strlen(buf) + 1, NULL, NULL))
342  rtn = false;
343  if(fd != hLoopback)
344  CloseHandle(fd);
345 #else
346  buf[len] = 0;
347  if(::write(fd, buf, len) < len)
348  rtn = false;
349  ::close(fd);
350 #endif
351  return rtn;
352 }
353 
354 bool control::state(const char *state)
355 {
356 #ifdef _MSWINDOWS_
357  return false;
358 #else
359  char buf[256], buf1[256];
360 
361  String::set(buf, sizeof(buf), _STR(path("prefix") + "/states/" + state + ".xml"));
362  if(!fsys::is_file(buf))
363  return false;
364  String::set(buf1, sizeof(buf1), _STR(path("prefix") + "state.xml"));
365  remove(buf1);
366  if(!stricmp(state, "up") || !stricmp(state, "none"))
367  return true;
368 
369 #ifdef HAVE_SYMLINK
370  if(symlink(buf, buf1))
371  return false;
372 #else
373  if(link(buf, buf1))
374  return false;
375 #endif
376  return true;
377 #endif
378 }
379 
380 FILE *control::output(const char *id)
381 {
382 #ifdef _MSWINDOWS_
383  if(!id)
384  return NULL;
385 
386  return fopen(_STR(path("controls") + "/" + id + ".out"), "w");
387 #else
388  if(replytarget && isdigit(*replytarget))
389  return fopen(path("reply") + str((Unsigned)atol(replytarget)), "w");
390  if(!id)
391  return NULL;
392  return fopen(_STR(path("controls") + "/" + id), "w");
393 #endif
394 }
395 
396 } // namespace sipwitch
Used for definitions of plugin modules.
static String path(const char *id)
Get a string from a server environment variable.
Definition: control.h:141
static void reply(const char *error=NULL)
Used by the server to send replies back to control requests.
Definition: control.cpp:218
static size_t attach(shell_t *env)
Creates the control fifo using server configuration.
Definition: control.cpp:61
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 release(void)
Used by the server to destroy the control fifo.
Definition: control.cpp:81
static bool static char * receive(void)
Used by the server to pull pending fifo requests.
Definition: control.cpp:87
static bool static FILE * output(const char *id)
Used to open an output session for returning control data.
Definition: control.cpp:380
static bool libexec(const char *fmt,...) __PRINTF(1
Execute an external shell command on behalf of the server.
Definition: control.cpp:261
static bool state(const char *value)
Sets server run state configuration.
Definition: control.cpp:354
Service configuration and component callbacks.
static shell * args
Definition: control.h:61
Manage control interface.
static const char * env(const char *id)
Return the value of a server environment variable.
Definition: control.h:131