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
18
19 #ifndef _MSWINDOWS_
20
21 #include <signal.h>
22 #include <sys/wait.h>
23 #include <syslog.h>
24 #include <fcntl.h>
25 #include <pwd.h>
26 #include <grp.h>
27 #include <sys/time.h>
28 #include <sys/resource.h>
29 #include <limits.h>
30 #include <errno.h>
31
32 #endif
33
34 namespace sipwitch {
35
36 static shell::flagopt helpflag('h',"--help", _TEXT("display this list"));
37 static shell::flagopt althelp('?', NULL, NULL);
38 static shell::stringopt iface('A', "--address", _TEXT("sip interface address"), "address", NULL);
39 static shell::numericopt port('P', "--port", _TEXT("sip port to bind"), "port", 5060);
40 static shell::flagopt backflag('b', "--background", _TEXT("run in background"));
41 static shell::flagopt altback('d', NULL, NULL);
42 static shell::flagopt dump('D', "--dump-config", _TEXT("show configuration"));
43 static shell::numericopt concurrency('c', "--concurrency", _TEXT("process concurrency"), "level");
44 static shell::flagopt desktop(0, "--desktop", _TEXT("enable desktop access"));
45 static shell::flagopt foreflag('f', "--foreground", _TEXT("run in foreground"));
46 #ifdef HAVE_PWD_H
47 static shell::stringopt group('g', "--group", _TEXT("use specified group permissions"), "groupid", NULL);
48 #endif
49 static shell::numericopt histbuf('h', "--history", _TEXT("set history buffer"), "count", 0);
50 static shell::stringopt loglevel('L', "--logging", _TEXT("set log level"), "level", "err");
51 static shell::stringopt loading('l', "--plugins", _TEXT("specify modules to load"), "names", "none");
52 static shell::flagopt nolocalusers('n', "--no-localusers", _TEXT("disable local user accounts"));
53 static shell::counteropt priority('p', "--priority", _TEXT("set priority level"), "level");
54 static shell::flagopt hotspot(0, "--public", _TEXT("public access mode"));
55 static shell::flagopt restart('r', "--restartable", _TEXT("set to restartable process"));
56 static shell::flagopt sservice('S', "--service", _TEXT("system service mode"));
57 static shell::flagopt trace('t', "--trace", _TEXT("trace sip messages"));
58 #ifdef HAVE_PWD_H
59 static shell::stringopt user('u', "--user", _TEXT("user to run as"), "userid", NULL);
60 #endif
61 static shell::flagopt verbose('v', NULL, _TEXT("set verbosity, can be used multiple times"), false);
62 static shell::flagopt version(0, "--version", _TEXT("show version information"));
63 static shell::numericopt debuglevel('x', "--debug", _TEXT("set debug level directly"), "level", 0);
64
65 static shell::groupopt groupconfig(_TEXT("User Options"));
66 static shell::stringopt configpath(0, "--configpath", _TEXT("config file"), "path", NULL);
67 static shell::stringopt cachepath(0, "--cachepath", _TEXT("cache files"), "dir", NULL);
68 static shell::stringopt prefixpath(0, "--prefixpath", _TEXT("provisioning files"), "dir", NULL);
69
70 #if defined(HAVE_SETRLIMIT) && defined(DEBUG)
71 #include <sys/time.h>
72 #include <sys/resource.h>
73
74 static void corefiles(void)
75 {
76 struct rlimit core;
77
78 assert(getrlimit(RLIMIT_CORE, &core) == 0);
79 #ifdef MAX_CORE_SOFT
80 core.rlim_cur = MAX_CORE_SOFT;
81 #else
82 core.rlim_cur = RLIM_INFINITY;
83 #endif
84 #ifdef MAX_CORE_HARD
85 core.rlim_max = MAX_CORE_HARD;
86 #else
87 core.rlim_max = RLIM_INFINITY;
88 #endif
89 assert(setrlimit(RLIMIT_CORE, &core) == 0);
90 }
91 #else
92 static void corefiles(void)
93 {
94 }
95 #endif
96
97 #ifdef HAVE_PWD_H
98 static const char *userpath(const char *path)
99 {
100 if(!path)
101 return NULL;
102
103 #ifndef _MSWINDOWS_
104 if(eq(path, "~/", 2) && getuid()) {
105 const char *home = getenv("HOME");
106 if(home)
107 return strdup(str(home) + ++path);
108 }
109 #endif
110 return path;
111 }
112 #endif
113
114 static void usage(void)
115 {
116 #if defined(DEBUG)
117 printf("%s\n", _TEXT("Usage: sipw [debug] [options]"));
118 #else
119 printf("%s\n", _TEXT("Usage: sipw [options]"));
120 #endif
121 printf("%s\n\n", _TEXT("Start sipwitch service"));
122 printf("%s\n", _TEXT("Options:"));
123 shell::help();
124 #if defined(DEBUG)
125 printf("%s", _TEXT(
126 "\nDebug Options:\n"
127 " --dbg execute command in debugger\n"
128 " --memcheck execute with valgrind memory check\n"
129 " --memleak execute with valgrind leak detection\n"
130 "\n"
131 ));
132 #endif
133
134 printf("\n%s\n", _TEXT("Report bugs to sipwitch-devel@gnu.org"));
135 exit(0);
136 }
137
138 static void versioninfo(void)
139 {
140 printf("SIP Witch " VERSION "\n%s", _TEXT(
141 "Copyright (C) 2007,2008,2009 David Sugar, Tycho Softworks\n"
142 "License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n"
143 "This is free software: you are free to change and redistribute it.\n"
144 "There is NO WARRANTY, to the extent permitted by law.\n"));
145 exit(0);
146 }
147
148 static void dumpconfig(void)
149 {
150 const char *dirpath =
control::env(
"users");
// /etc/sipwitch.d
151 if(!dirpath)
153
158 printf("provision: %s\n", dirpath);
159 exit(0);
160 }
161
162 static bool errlog(shell::loglevel_t level, const char *text)
163 {
164 switch(level) {
165 case shell::WARN:
167 break;
168 case shell::FAIL:
170 break;
171 case shell::ERR:
173 default:
174 break;
175 }
178 return false;
179 }
180
181 static void up(const char *pidfile)
182 {
183 if(pidfile) {
184 ::remove(pidfile);
185 FILE *fp = fopen(pidfile, "w");
186 if(fp) {
187 fprintf(fp, " %ld\n", (long)getpid());
188 fclose(fp);
189 fp = NULL;
190 }
191 }
192
196
197 if(is(trace))
199
204
210
211 if(pidfile)
212 ::remove(pidfile);
213 }
214
215
216 static void init(
int argc,
char **argv,
bool detached, shell::mainproc_t svc = NULL)
217 {
218 secure::init();
219
220 bool daemon = true;
221 const char *cp;
222 const char *prefix;
223 const char *rundir;
224 const char *plugins = DEFAULT_LIBPATH "/sipwitch";
225 shell args;
226
227 shell::bind("sipwitch");
228 corefiles();
229 args.getargv0(argv);
230
231 const char *security = args.getenv("SECURITY");
232 if(!security)
233 security = "default";
234
235 #if defined(DEBUG)
236 if(eq(argv[1], "-gdb") || eq(argv[1], "--gdb") || eq(argv[1], "-dbg") || eq(argv[1], "--dbg")) {
237 char *dbg[] = {(char *)"gdb", (char *)"--args", NULL};
238 const char *cp = args.getenv("DEBUGGER");
239 if(cp && *cp)
240 dbg[0] = (char *)cp;
241 args.restart(argv[0], &argv[2], dbg);
242 }
243
244 if(eq(argv[1], "-memcheck") || eq(argv[1], "--memcheck")) {
245 char *mem[] = {(char *)"valgrind", (char *)"--tool=memcheck", NULL};
246 args.restart(argv[0], &argv[2], mem);
247 }
248
249 if(eq(argv[1], "-memleak") || eq(argv[1], "--memleak")) {
250 char *mem[] = {(char *)"valgrind",
251 (char *)"--tool=memcheck", (char *)"--leak-check=yes", NULL};
252 args.restart(argv[0], &argv[2], mem);
253 }
254 #endif
255
256 // parse and check for help
257 args.parse(argc, argv);
258 if(is(helpflag) || is(althelp) || args.argc() > 0)
259 usage();
260
261 if(is(version))
262 versioninfo();
263
264 // cheat out shell parser...
265 // argv[0] = (char *)"sipwitch";
266
270
271 #ifdef _MSWINDOWS_
272 rundir = strdup(str(args.getenv("APPDATA")) + "/sipwitch");
273 prefix = args.execdir();
274 plugins = args.execdir();
275 args.setsym("config", _STR(str(prefix) + "/sipwitch.ini"));
276 args.setsym("controls", rundir);
277 args.setsym("control", "\\\\.\\mailslot\\sipwitch_ctrl");
278 args.setsym("cache", _STR(str(prefix) + "/cache"));
279 args.setsym("logfiles", _STR(str(prefix) + "/logs"));
280 args.setsym("siplogs", _STR(str(prefix) + "/logs/siptrace.log"));
281 args.setsym("logfile", _STR(str(prefix) + "/logs/sipwitch.log"));
282 args.setsym("calls", _STR(str(prefix) + "/logs/sipwitch.calls"));
283 args.setsym("stats", _STR(str(prefix) + "/logs/sipwitch.stats"));
284 args.setsym("prefix", rundir);
285 args.setsym("shell", "cmd.exe");
286 prefix = rundir;
287 #else
288 // if local build directory image being executed directly...
289 const char *dp = strrchr(args.execdir(), '/');
290 if(dp && (eq(dp, "/.") || eq(dp, "/server") || eq(dp, "/.libs")))
291 plugins = args.execdir();
292
293 prefix = DEFAULT_VARPATH "/lib/sipwitch";
294 rundir = DEFAULT_VARPATH "/run/sipwitch";
295 args.setsym("reply", "/tmp/.sipwitch.");
296
297 args.setsym("config", DEFAULT_CFGPATH "/sipwitch.conf");
298 args.setsym("cache", DEFAULT_VARPATH "/cache/sipwitch");
299 args.setsym("controls", DEFAULT_VARPATH "/run/sipwitch");
300 args.setsym("control", DEFAULT_VARPATH "/run/sipwitch/control");
301 args.setsym("pidfile", DEFAULT_VARPATH "/run/sipwitch/pidfile");
302 args.setsym("events", DEFAULT_VARPATH "/run/sipwitch/events");
303 args.setsym("config", DEFAULT_CFGPATH "/sipwitch.conf");
304 args.setsym("logfiles", DEFAULT_VARPATH "/log");
305 args.setsym("siplogs", DEFAULT_VARPATH "/log/siptrace.log");
306 args.setsym("logfile", DEFAULT_VARPATH "/log/sipwitch.log");
307 args.setsym("calls", DEFAULT_VARPATH "/log/sipwitch.calls");
308 args.setsym("stats", DEFAULT_VARPATH "/log/sipwitch.stats");
309 args.setsym("prefix", prefix);
310 args.setsym("shell", "/bin/sh");
311 #endif
312
313 #ifdef HAVE_PWD_H
314 struct passwd *pwd = getpwuid(getuid());
315 umask(007);
316
317 if(getuid() && pwd && pwd->pw_dir && *pwd->pw_dir == '/') {
318 args.setsym("prefix", pwd->pw_dir);
319 if(!eq(pwd->pw_shell, "/bin/false") && !eq(pwd->pw_dir, "/var/", 5) && !eq(pwd->pw_dir, "/srv/", 5)) {
320 umask(077);
321 daemon = false;
322 };
323 }
324
325 if(!daemon && pwd) {
326 rundir = strdup(str("/tmp/sipwitch-") + str(pwd->pw_name));
327 prefix = strdup(str(pwd->pw_dir) + "/.sipwitch");
328
329 args.setsym(
"statmap", _STR(str(
STAT_MAP "-") + str(pwd->pw_name)));
330 args.setsym(
"callmap", _STR(str(
CALL_MAP "-") + str(pwd->pw_name)));
331 args.setsym(
"regmap", _STR(str(
REGISTRY_MAP "-") + str(pwd->pw_name)));
332
333 cp = userpath(*configpath);
334 if(is(configpath) && fsys::is_file(cp))
335 args.setsym("config", cp);
336 else
337 args.setsym("config", _STR(str(pwd->pw_dir) + "/.sipwitchrc"));
338
339 cp = userpath(*cachepath);
340 if(is(cachepath) && fsys::is_dir(cp))
341 args.setsym("cache", cp);
342 else
343 args.setsym("cache", _STR(str(rundir) + "/cache"));
344
345 args.setsym("controls", rundir);
346 args.setsym("control", _STR(str(rundir) + "/control"));
347 args.setsym("events", _STR(str(rundir) + "/events"));
348 args.setsym("pidfile", _STR(str(rundir) + "/pidfile"));
349 args.setsym("logfiles", rundir);
350 args.setsym("siplogs", _STR(str(rundir) + "/siplogs"));
351 args.setsym("logfile", _STR(str(rundir) + "/logfile"));
352 args.setsym("calls", _STR(str(rundir) + "/calls"));
353 args.setsym("stats", _STR(str(rundir) + "/stats"));
354
355
356 cp = userpath(*prefixpath);
357 if(is(prefixpath) && fsys::is_dir(cp))
358 args.setsym("prefix", cp);
359 else
360 args.setsym("prefix", prefix);
361
362 args.setsym("shell", pwd->pw_shell);
363 }
364
365 #else
366 if(argv[1])
367 daemon = false;
368 #endif
369
370 #ifdef HAVE_PWD_H
371 cp = args.getenv("GROUP");
372 if(cp && *cp && !is(group))
373 group.set(cp);
374
375 cp = args.getenv("USER");
376 if(cp && *cp && !is(user))
377 user.set(cp);
378
379 // root gets these from default to act as user daemon...
380
381 if(!getuid()) {
382 cp = getenv("FIRSTUID");
383 if(!cp)
384 cp = getenv("UID");
385
386 if(cp && *cp)
388
389 cp = getenv("SIPUSERS");
390 if(cp && *cp)
392
393 cp = getenv("SIPADMIN");
394 if(cp && *cp)
396 }
397
398 if(is(nolocalusers)) {
401 }
402
403 #endif
404
405 cp = args.getenv("CONCURRENCY");
406 if(cp && *cp)
407 concurrency.set(atol(cp));
408
409 cp = args.getenv("PRIORITY");
410 if(cp && *cp)
411 priority.set(atol(cp));
412
413 cp = args.getenv("VERBOSE");
414 if(cp && *cp)
415 loglevel.set(strdup(cp));
416
417 cp = args.getenv("LOGGING");
418 if(cp && *cp)
419 loglevel.set(strdup(cp));
420
421 cp = args.getenv("LOGGING");
422 if(cp && *cp)
423 histbuf.set(atoi(cp));
424
425 cp = args.getenv("PLUGINS");
426 if(cp && *cp)
427 loading.set(strdup(cp));
428
429 if(is(dump)) {
431 dumpconfig();
432 }
433
434 // check validity of some options...
435
436 if(*concurrency < 0)
437 shell::errexit(1, "sipw: concurrency: %ld: %s\n",
438 *concurrency, _TEXT("negative levels invalid"));
439
440 if(*histbuf < 0)
441 shell::errexit(1, "sipw: history: %ld: %s\n",
442 *histbuf, _TEXT("negative buffer limit invalid"));
443
444 // bind sip interface and port from command line options...
445 // use xx:..:xx for ipv6 address, or a.b.c.d for ipv4
446
447 if(is(iface))
449
450 if(is(port))
452
453 // set threading properties...
454
455 if(*concurrency > 0)
456 Thread::concurrency(*concurrency);
457
458 shell::priority(*priority);
459
460 #ifdef SCHED_RR
461 if(*priority > 0)
462 Thread::policy(SCHED_RR);
463 #endif
464
465 // fore and background...
466
467 if(is(backflag) || is(altback))
468 daemon = true;
469
470 if(is(foreflag))
471 daemon = false;
472
473 // lets play with verbose level and logging options
474
475 if(is(verbose))
476 verbose.set(*verbose + (unsigned)shell::INFO);
477 else {
478 if(atoi(*loglevel) > 0)
479 verbose.set(atoi(*loglevel));
480 else if(eq(*loglevel, "0") || eq(*loglevel, "no", 2) || eq(*loglevel, "fail", 4))
481 verbose.set((unsigned)shell::FAIL);
482 else if(eq(*loglevel, "err", 3))
483 verbose.set((unsigned)shell::ERR);
484 else if(eq(*loglevel, "warn", 4))
485 verbose.set((unsigned)shell::WARN);
486 else if(eq(*loglevel, "noti", 4))
487 verbose.set((unsigned)shell::NOTIFY);
488 else if(eq(*loglevel, "info"))
489 verbose.set((unsigned)shell::INFO);
490 else if(eq(*loglevel, "debug", 5))
491 verbose.set((unsigned)shell::DEBUG0 + atoi(*loglevel + 5));
492 }
493
494 if(is(debuglevel))
495 verbose.set((unsigned)shell::DEBUG0 + *debuglevel);
496
497 if(is(hotspot) || eq(security, "public"))
499
500 #ifdef HAVE_PWD_H
501 pwd = NULL;
502 struct group *grp = NULL;
503
504 // if root user, then see if we change permissions...
505
506 if(!getuid()) {
507 if(*user) {
508 if(atoi(*user))
509 pwd = getpwuid(atoi(*user));
510 else
511 pwd = getpwnam(*user);
512 if(!pwd)
513 shell::errexit(2, "*** sipw: %s: %s\n", *user,
514 _TEXT("unknown or invalid user id"));
515 }
516 }
517
518 if(*group) {
519 if(atoi(*group))
520 grp = getgrgid(atoi(*group));
521 else
522 grp = getgrnam(*group);
523 if(!grp)
524 shell::errexit(2, "*** sipw: %s: %s\n", *group,
525 _TEXT("unknown or invalid group id"));
526 }
527
528 if(grp) {
529 #ifdef HAVE_SETGROUPS
530 setgroups(0, NULL);
531 #endif
532 umask(007);
533 if(setgid(grp->gr_gid))
534 shell::errlog("*** sipw: %u: %s\n", grp->gr_gid,
535 _TEXT("cannot set group"));
536 }
537
538 int uid = 0;
539
540 if(pwd) {
541 #ifdef HAVE_SETGROUPS
542 setgroups(0, NULL);
543 #endif
544 umask(007);
545 if(!grp) {
546 if(setgid(pwd->pw_gid))
547 shell::errlog("*** sip: %u: %s\n", pwd->pw_gid,
548 _TEXT("cannot set group"));
549 }
550 uid = pwd->pw_uid;
551 }
552
553 endgrent();
554 endpwent();
555
556 if(is(desktop) || eq(security, "desktop")) {
557 umask(002);
559 }
560
561 #endif
562
563 dir::create(rundir, fsys::GROUP_PUBLIC);
564 dir::create(prefix, fsys::GROUP_PRIVATE);
565 dir::create(args.getsym("cache"), fsys::GROUP_PRIVATE);
566
567 if(fsys::prefix(prefix))
568 shell::errexit(3, "*** sip: %s: %s\n",
569 prefix, _TEXT("data directory unavailable"));
570
571 shell::loglevel_t level = (shell::loglevel_t)*verbose;
573
574 #ifdef HAVE_SYSTEMD
575 if(is(sservice)) // systemd services no need for fork...
576 detached = true;
577 #endif
578
579 // daemonify process....
580 if(daemon) {
581 if(!detached)
582 args.detach(svc);
584 }
585 else
587
590
591 const char *home = getenv("HOME");
592
593 #ifdef _MSWINDOWS_
594 if(!home)
595 home = getenv("USERPROFILE");
596 #endif
597
598 if(!home)
599 home = args.getenv("prefix");
600
601 args.setsym("HOME", home);
602
604 shell::errexit(1, "*** sipw: %s\n",
605 _TEXT("no control file; exiting"));
606
607 // drop root privilege
608 #ifdef HAVE_PWD_H
609 if(uid) {
610 if(setuid(uid))
611 shell::errlog("*** sipw: %u: %s\n", uid,
612 _TEXT("cannot set user"));
613 }
614 #endif
615
616 if(is(restart))
617 args.restart();
618
619 up(args.getsym("pidfile"));
620 #ifdef HAVE_SYSTEMD
621 if(is(sservice)) {
622 sd_notify(0, "READY=1");
623 }
624 #endif
625 }
626
627 } // end namespace
628
629 using namespace sipwitch;
630
631 // stub code for windows service daemon...
632
633 static SERVICE_MAIN(
main, argc, argv)
634 {
636 init(argc, argv, true);
637 }
638
639 // program main
640
642 {
643 init(argc, argv, false, &service_main);
645 }
static const char * sipusers
static void failure(const char *reason)
Send error to user.
static void startup(void)
static size_t attach(shell_t *env)
Creates the control fifo using server configuration.
static void bind(unsigned short port)
static void add(shell::loglevel_t lid, const char *msg)
static void release(void)
Used by the server to destroy the control fifo.
static void config(shell *envp)
static void enableDumping(void)
static void terminate(const char *reason)
Notify server termination.
static void shutdown(void)
static void errlog(shell::loglevel_t level, const char *text)
Module access to error logging system.
static bool start(void)
Start server event system by binding event session listener.
static void warning(const char *reason)
Send warning to user.
static void setPublic(void)
static const char * sipadmin
static shell::logmode_t logmode
void set(shell::loglevel_t lid, const char *msg)
static void service(const char *name)
static void plugins(const char *argv0, const char *names)
static const char * env(const char *id)
Return the value of a server environment variable.