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>
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
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
62 {
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
82 {
83 shell::log(shell::INFO, "shutdown");
84 cleanup();
85 }
86
88 {
89 static char buf[512];
90 char *cp;
91
92 if(!fifo)
93 return NULL;
94
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
141 {
142 char buf[64];
143
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
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
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
211 {
212 shell::log(shell::INFO, "shutdown");
213 cleanup();
214 }
215
216 #endif
217
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
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
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
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
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.
static void reply(const char *error=NULL)
Used by the server to send replies back to control requests.
static size_t attach(shell_t *env)
Creates the control fifo using server configuration.
static bool send(const char *format,...) __PRINTF(1
Send a printf-style message to the control fifo via the file system.
static void release(void)
Used by the server to destroy the control fifo.
static bool static char * receive(void)
Used by the server to pull pending fifo requests.
static bool static FILE * output(const char *id)
Used to open an output session for returning control data.
static bool libexec(const char *fmt,...) __PRINTF(1
Execute an external shell command on behalf of the server.
static bool state(const char *value)
Sets server run state configuration.
Service configuration and component callbacks.
Manage control interface.
static const char * env(const char *id)
Return the value of a server environment variable.