1 /* zxbusd.c - Audit bus daemon using STOMP 1.1
2 * Copyright (c) 2006,2012 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.
3 * This is confidential unpublished proprietary source code of the author.
4 * NO WARRANTY, not even implied warranties. Contains trade secrets.
5 * Distribution prohibited unless authorized in writing. See file COPYING.
6 * Special grant: zxbusd.c may be used with zxid open source project under
7 * same licensing terms as zxid itself.
8 * $Id$
9 *
10 * 15.4.2006, started work over Easter holiday --Sampo
11 * 22.4.2006, added more options over the weekend --Sampo
12 * 16.8.2012, modified license grant to allow use with ZXID.org --Sampo
13 *
14 * This file contains option processing, configuration, and main().
15 *
16 * To create bus users, you should follow these steps
17 * 1. Run ./zxbuslist -c 'BURL=https://sp.foo.com/' -dc to determine the entity ID
18 * 2. Convert entity ID to SHA1 hash: ./zxcot -p 'https://sp.foo.com?o=B'
19 * 3. Create the user: ./zxpasswd -a 'eid: https://sp.foo.com?o=B' -new G2JpTSX_dbdJ7frhYNpKWGiMdTs /var/zxid/bus/uid/ <passwd
20 * 4. To enable ClientTLS authentication, determine the subject_hash of
21 * the encryption certificate and symlink that to the main account:
22 * > openssl x509 -subject_hash -noout </var/zxid/buscli/pem/enc-nopw-cert.pem
23 * 162553b8
24 * > ln -s /var/zxid/bus/uid/G2JpTSX_dbdJ7frhYNpKWGiMdTs /var/zxid/bus/uid/162553b8
25 */
26
27 #include <pthread.h>
28 #include <signal.h>
29 #include <fcntl.h>
30 #include <netdb.h>
31 #include <string.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <sys/wait.h>
39
40 #ifdef HAVE_NET_SNMP
41 #include "snmpInterface.h"
42 #endif
43
44 /*#include "dialout.h" / * Async serial support */
45 /*#include "serial_sync.h" / * Sync serial support */
46 #include "errmac.h"
47 #include "hiios.h"
48 #include "hiproto.h"
49 #include "akbox.h"
50 #include "c/zxidvers.h"
51 #include <zx/zxid.h>
52 #include <zx/zxidutil.h>
53
54 #define ZXBUS_PATH "/var/zxid/bus/"
55
56 const char* help =
57 "zxbusd - Audit bus daemon using STOMP 1.1 - R" ZXID_REL "\n\
58 Copyright (c) 2006,2012 Sampo Kellomaki (sampo@iki.fi), All Rights Reserved.\n\
59 NO WARRANTY, not even implied warranties.\n\
60 Send well researched bug reports to the author. Home: zxid.org\n\
61 \n\
62 Usage: zxbusd [options] PROTO:REMOTEHOST:PORT\n\
63 echo secret | zxbusd -p sis::5066 -c AES256 -k 0 dts:quebec.cellmail.com:5067\n\
64 echo secret | zxbusd -p sis::5066 -c AES256 -k 0 dts:/dev/se_hdlc1:S-9600-1000-8N1\n\
65 zxbusd -p smtp::25 sis:localhost:5066 smtp:mail.cellmail.com:25\n\
66 -c CONF Optional configuration string (default -c PATH=" ZXBUS_PATH ")\n\
67 Most of the configuration is read from " ZXBUS_PATH ZXID_CONF_FILE "\n\
68 -cp PATH Path for message and user databases. Default: " ZXBUS_PATH "\n\
69 -p PROT:IF:PORT Protocol, network interface and TCP port for listening\n\
70 connections. If you omit interface, all interfaces are bound.\n\
71 stomp:0.0.0.0:2229 - Listen for STOMP 1.1 (default if no -p supplied)\n\
72 smtp:0.0.0.0:25 - Listen for SMTP (RFC 2821)\n\
73 http:0.0.0.0:80 - Listen for HTTP/1.0 (simplified)\n\
74 tp:0.0.0.0:5068 - Listen for test ping protocol\n\
75 -t SECONDS Connection timeout. Default: 0=no timeout.\n\
76 -cy CIPHER Enable crypto on DTS interface using specified cipher. Use '?' for list.\n\
77 -k FDNUMBER File descriptor for reading symmetric key. Use 0 for stdin.\n\
78 -nfd NUMBER Maximum number of file descriptors, i.e. simultaneous\n\
79 connections. Default 20 (about 16 connections).\n\
80 -npdu NUMBER Maximum number of simultaneously active PDUs. Default 60.\n\
81 -nch NUMBER Maximum number of subscribable channels. Default 10.\n\
82 -nthr NUMBER Number of threads. Default 1. Should not exceed number of CPUs.\n\
83 -nkbuf BYTES Size of kernel buffers. Default is not to change kernel buffer size.\n\
84 -nlisten NUMBER Listen backlog size. Default 128.\n\
85 -egd PATH Specify path of Entropy Gathering Daemon socket, default on\n\
86 Solaris: /tmp/entropy. On Linux /dev/urandom is used instead\n\
87 See http://www.lothar.com/tech/crypto/ or\n\
88 http://www.aet.tu-cottbus.de/personen/jaenicke/postfix_tls/prngd.html\n\
89 -rand PATH Location of random number seed file. On Solaris EGD is used.\n\
90 On Linux the default is /dev/urandom. See RFC1750.\n\
91 -snmp PORT Enable SNMP agent (if compiled with Net SNMP).\n\
92 -uid UID:GID If run as root, drop privileges and assume specified uid and gid.\n\
93 -pid PATH Write process id in the supplied path\n\
94 -watchdog Enable built-in watch dog\n\
95 -kidpid PATH Write process id of the child of watchdog in the supplied path\n\
96 -ak size_MB Turn on Application Flight Recorder. size_MB is per thread buffer.\n\
97 -v Verbose messages.\n\
98 -q Be extra quiet.\n\
99 -d Turn on debugging.\n\
100 -dc Dump config.\n\
101 -license Show licensing details\n\
102 -h This help message\n\
103 -- End of options\n\
104 N.B. Although zxbusd is a 'daemon', it does not daemonize itself. You can always say zxbusd&\n";
105
106 char* instance = "zxbusd"; /* how this server is identified in logs */
107 char* zxbus_path = ZXBUS_PATH;
108 zxid_conf* zxbus_cf;
109 int ak_buf_size = 0;
110 int verbose = 1;
111 extern int errmac_debug;
112 int debugpoll = 0;
113 int timeout = 0;
114 int nfd = 20;
115 int npdu = 60;
116 int nch = 10;
117 int nthr = 1;
118 int nkbuf = 0;
119 int listen_backlog = 128; /* what is right tuning for this? */
120 int gcthreshold = 0;
121 int leak_free = 0;
122 //int assert_nonfatal = 0;
123 int drop_uid = 0;
124 int drop_gid = 0;
125 int watchdog;
126 int snmp_port = 0;
127 char* pid_path = 0;
128 char* kidpid_path = 0;
129 char* rand_path;
130 char* egd_path;
131 char symmetric_key[1024];
132 int symmetric_key_len;
133 struct hi_host_spec* listen_ports = 0;
134 struct hi_host_spec* remotes = 0;
135
136 struct hi_proto hi_prototab[] = { /* n.b. order in this table must match constants in hiproto.h */
137 { "dummy0", 0, 0, 0 },
138 { "pollon", 0, 0, 0 },
139 { "sis", 5066, 0, 0 },
140 { "dts", 5067, 0, 0 },
141 { "smtp", 25, 0, 0 },
142 { "http", 8080, 0, 0 },
143 { "tp", 5068, 0, 0 }, /* testping (6) */
144 { "stomp", 2228, 0, 0 }, /* 7 */
145 { "stomps", 2229, 1, 0 }, /* 8 n.b. 2229 is zxbus assigned port. Normal STOMP port is 61613 */
146 { "", 0 }
147 };
148
149 char remote_station_addr[] = { 0x61, 0x89, 0x00, 0x00 }; /* *** temp kludge */
150 struct hiios* shuff; /* Main I/O shuffler object (global to help debugging) */
151
152 #define SNMPLOGFILE "/var/zxid/log/snmp.log"
153
154 /* proto:host:port or proto:host or proto::port */
155
156 /* Called by: opt x2 */
parse_port_spec(char * arg,struct hi_host_spec ** head,char * default_host)157 int parse_port_spec(char* arg, struct hi_host_spec** head, char* default_host)
158 {
159 struct hostent* he;
160 char prot[8];
161 char host[256];
162 int proto, port, ret;
163 struct hi_host_spec* hs;
164
165 ret = sscanf(arg, "%8[^:]:%255[^:]:%i", prot, host, &port);
166 switch (ret) {
167 case 2:
168 port = -1; /* default */
169 case 3:
170 if (!strlen(prot)) {
171 ERR("Bad proto:host:port spec(%s). You MUST specify proto.", arg);
172 exit(5);
173 }
174 for (proto = 0; hi_prototab[proto].name[0]; ++proto)
175 if (!strcmp(hi_prototab[proto].name, prot))
176 break;
177 if (!hi_prototab[proto].name[0]) {
178 ERR("Bad proto:host:port spec(%s). Unknown proto.", arg);
179 exit(5);
180 }
181 if (port == -1)
182 port = hi_prototab[proto].default_port;
183 if (strlen(host))
184 default_host = host;
185 break;
186 default:
187 ERR("Bad proto:host:port spec(%s). %d", arg, ret);
188 return 0;
189 }
190
191 D("arg(%s) parsed as proto(%s)=%d host(%s) port(%d)", arg, prot, proto, host, port);
192 ZMALLOC(hs);
193
194 if (default_host[0] == '/') { /* Its a serial port */
195 hs->sin.sin_family = (unsigned short int)0xfead;
196 } else {
197 he = gethostbyname(default_host);
198 if (!he) {
199 ERR("hostname(%s) did not resolve(%d)", default_host, h_errno);
200 exit(5);
201 }
202
203 hs->sin.sin_family = AF_INET;
204 hs->sin.sin_port = htons(port);
205 memcpy(&(hs->sin.sin_addr.s_addr), he->h_addr, sizeof(hs->sin.sin_addr.s_addr));
206 }
207 hs->specstr = arg;
208 hs->proto = proto;
209 hs->next = *head;
210 *head = hs;
211 return 1;
212 }
213
214 /* Called by: main x8, zxbusd_main, zxbuslist_main, zxbustailf_main, zxcall_main, zxcot_main, zxdecode_main */
opt(int * argc,char *** argv,char *** env)215 void opt(int* argc, char*** argv, char*** env)
216 {
217 struct zx_str* ss;
218 if (*argc <= 1) goto argerr;
219
220 while (1) {
221 ++(*argv); --(*argc);
222
223 if (!(*argc) || ((*argv)[0][0] != '-')) break; /* probably the remote host and port */
224
225 switch ((*argv)[0][1]) {
226 case '-': if ((*argv)[0][2]) break;
227 ++(*argv); --(*argc);
228 DD("End of options by --");
229 return; /* -- ends the options */
230
231 case 'a': if ((*argv)[0][2] != 'k' || (*argv)[0][3]) break;
232 ++(*argv); --(*argc);
233 if (!(*argc)) break;
234 ak_buf_size = atoi((*argv)[0]);
235 ak_buf_size = ak_buf_size << 20; /* Mega bytes */
236 if (ak_buf_size)
237 ak_add_thread(ak_buf_size,1); /* Add current "main" thread. */
238 continue;
239
240 case 'n':
241 switch ((*argv)[0][2]) {
242 case 'f': if ((*argv)[0][3] != 'd' || (*argv)[0][4]) break;
243 ++(*argv); --(*argc);
244 if (!(*argc)) break;
245 nfd = atoi((*argv)[0]);
246 continue;
247 case 'c': if ((*argv)[0][3] != 'h' || (*argv)[0][4]) break;
248 ++(*argv); --(*argc);
249 if (!(*argc)) break;
250 nch = atoi((*argv)[0]);
251 continue;
252 case 'p': if ((*argv)[0][3] != 'd' || (*argv)[0][4] != 'u' || (*argv)[0][5]) break;
253 ++(*argv); --(*argc);
254 if (!(*argc)) break;
255 npdu = atoi((*argv)[0]);
256 continue;
257 case 't': if ((*argv)[0][3] != 'h' || (*argv)[0][4] != 'r' || (*argv)[0][5]) break;
258 ++(*argv); --(*argc);
259 if (!(*argc)) break;
260 nthr = atoi((*argv)[0]);
261 continue;
262 case 'k': if ((*argv)[0][3] != 'b' || (*argv)[0][4] != 'u' || (*argv)[0][5] != 'f' || (*argv)[0][6]) break;
263 ++(*argv); --(*argc);
264 if (!(*argc)) break;
265 nkbuf = atoi((*argv)[0]);
266 continue;
267 case 'l': if ((*argv)[0][3] != 'i' || (*argv)[0][4] != 's' || (*argv)[0][5]) break;
268 ++(*argv); --(*argc);
269 if (!(*argc)) break;
270 listen_backlog = atoi((*argv)[0]);
271 continue;
272 }
273 break;
274
275 case 's':
276 switch ((*argv)[0][2]) {
277 case 'n': if ((*argv)[0][3] != 'm' || (*argv)[0][4] != 'p' || (*argv)[0][5]) break;
278 ++(*argv); --(*argc);
279 if (!(*argc)) break;
280 snmp_port = atoi((*argv)[0]);
281 continue;
282 }
283 break;
284
285 case 't': if ((*argv)[0][2]) break;
286 ++(*argv); --(*argc);
287 if (!(*argc)) break;
288 timeout = atoi((*argv)[0]);
289 continue;
290
291 case 'd':
292 switch ((*argv)[0][2]) {
293 case '\0':
294 ++errmac_debug;
295 continue;
296 case 'p': if ((*argv)[0][3]) break;
297 ++debugpoll;
298 continue;
299 case 'i': if ((*argv)[0][3]) break;
300 ++(*argv); --(*argc);
301 if (!(*argc)) break;
302 instance = (*argv)[0];
303 continue;
304 case 'c':
305 ss = zxid_show_conf(zxbus_cf);
306 if (verbose>1) {
307 printf("\n======== CONF ========\n%.*s\n^^^^^^^^ CONF ^^^^^^^^\n",ss->len,ss->s);
308 exit(0);
309 }
310 fprintf(stderr, "\n======== CONF ========\n%.*s\n^^^^^^^^ CONF ^^^^^^^^\n",ss->len,ss->s);
311 continue;
312 }
313 break;
314
315 case 'v':
316 switch ((*argv)[0][2]) {
317 case '\0':
318 ++verbose;
319 continue;
320 }
321 break;
322
323 case 'q':
324 switch ((*argv)[0][2]) {
325 case '\0':
326 verbose = 0;
327 continue;
328 }
329 break;
330
331 case 'e':
332 switch ((*argv)[0][2]) {
333 case 'g': if ((*argv)[0][3] != 'd' || (*argv)[0][4]) break;
334 ++(*argv); --(*argc);
335 if (!(*argc)) break;
336 egd_path = (*argv)[0];
337 continue;
338 }
339 break;
340
341 case 'r':
342 switch ((*argv)[0][2]) {
343 case 'f':
344 AK_TS(LEAK, 0, "memory leaks enabled");
345 #if 1
346 ERR("*** WARNING: You have turned memory frees to memory leaks. We will (eventually) run out of memory. Using -rf is not recommended. %d\n", 0);
347 #endif
348 ++leak_free;
349 continue;
350 #if 0
351 case 'e':
352 if ((*argv)[0][3]) break;
353 ++(*argv); --(*argc);
354 if ((*argc) < 4) break;
355 sscanf((*argv)[0], "%i", &abort_funcno);
356 ++(*argv); --(*argc);
357 sscanf((*argv)[0], "%i", &abort_line);
358 ++(*argv); --(*argc);
359 sscanf((*argv)[0], "%i", &abort_error_code);
360 ++(*argv); --(*argc);
361 sscanf((*argv)[0], "%i", &abort_iter);
362 fprintf(stderr, "Will force core upon %x:%x err=%d iter=%d\n",
363 abort_funcno, abort_line, abort_error_code, abort_iter);
364 continue;
365 #endif
366 case 'g':
367 if ((*argv)[0][3]) break;
368 ++(*argv); --(*argc);
369 if (!(*argc)) break;
370 gcthreshold = atoi((*argv)[0]);
371 if (!gcthreshold)
372 ERR("*** WARNING: You have disabled garbage collection. This may lead to increased memory consumption for scripts that handle a lot of PDUs or run for long time. Using `-rg 0' is not recommended. %d\n", 0);
373 continue;
374 case 'a':
375 if ((*argv)[0][3] == 0) {
376 AK_TS(ASSERT_NONFATAL, 0, "assert nonfatal enabled");
377 #if 1
378 ERR("*** WARNING: YOU HAVE TURNED ASSERTS OFF USING -ra FLAG. THIS MEANS THAT YOU WILL NOT BE ABLE TO OBTAIN ANY SUPPORT. IF PROGRAM NOW TRIES TO ASSERT IT MAY MYSTERIOUSLY AND UNPREDICTABLY CRASH INSTEAD, AND NOBODY WILL BE ABLE TO FIGURE OUT WHAT WENT WRONG OR HOW MUCH DAMAGE MAY BE DONE. USING -ra IS NOT RECOMMENDED. %d\n", assert_nonfatal);
379 #endif
380 ++assert_nonfatal;
381 continue;
382 }
383 if (!strcmp((*argv)[0],"-rand")) {
384 ++(*argv); --(*argc);
385 if (!(*argc)) break;
386 rand_path = (*argv)[0];
387 continue;
388 }
389 break;
390 }
391 break;
392
393 case 'w':
394 switch ((*argv)[0][2]) {
395 case 'a':
396 if (!strcmp((*argv)[0],"-watchdog")) {
397 ++watchdog;
398 continue;
399 }
400 break;
401 }
402 break;
403
404 case 'p':
405 switch ((*argv)[0][2]) {
406 case '\0':
407 ++(*argv); --(*argc);
408 if (!(*argc)) break;
409 if (!parse_port_spec((*argv)[0], &listen_ports, "0.0.0.0")) break;
410 continue;
411 case 'i':
412 if (!strcmp((*argv)[0],"-pid")) {
413 ++(*argv); --(*argc);
414 if (!(*argc)) break;
415 pid_path = (*argv)[0];
416 continue;
417 }
418 break;
419 }
420 break;
421
422 case 'k':
423 switch ((*argv)[0][2]) {
424 case 'i':
425 if (!strcmp((*argv)[0],"-kidpid")) {
426 ++(*argv); --(*argc);
427 if (!(*argc)) break;
428 kidpid_path = (*argv)[0];
429 continue;
430 }
431 break;
432 case '\0':
433 ++(*argv); --(*argc);
434 if (!(*argc)) break;
435 read_all_fd(atoi((*argv)[0]), symmetric_key, sizeof(symmetric_key), &symmetric_key_len);
436 D("Got %d characters of symmetric key", symmetric_key_len);
437 continue;
438 }
439 break;
440
441 case 'c':
442 switch ((*argv)[0][2]) {
443 case '\0':
444 ++(*argv); --(*argc);
445 if ((*argc) < 1) break;
446 zxid_parse_conf(zxbus_cf, (*argv)[0]);
447 continue;
448 case 'y':
449 ++(*argv); --(*argc);
450 if (!(*argc)) break;
451 #ifndef ENCRYPTION
452 ERR("Encryption not compiled in. %d",0);
453 #endif
454 continue;
455 case 'p':
456 ++(*argv); --(*argc);
457 if (!(*argc)) break;
458 zxbus_path = (*argv)[0];
459 continue;
460 }
461 break;
462
463 case 'u':
464 switch ((*argv)[0][2]) {
465 case 'i': if ((*argv)[0][3] != 'd' || (*argv)[0][4]) break;
466 ++(*argv); --(*argc);
467 if (!(*argc)) break;
468 sscanf((*argv)[0], "%i:%i", &drop_uid, &drop_gid);
469 continue;
470 }
471 break;
472
473 case 'l':
474 switch ((*argv)[0][2]) {
475 case 'i':
476 if (!strcmp((*argv)[0],"-license")) {
477 extern char* license;
478 fprintf(stderr, "%s", license);
479 exit(0);
480 }
481 break;
482 }
483 break;
484
485 }
486 /* fall thru means unrecognized flag */
487 if (*argc)
488 fprintf(stderr, "Unrecognized flag `%s'\n", (*argv)[0]);
489 argerr:
490 fprintf(stderr, "%s", help);
491 exit(3);
492 }
493
494 #if 0
495 /* Remaining commandline is the remote host spec for DTS */
496 while (*argc) {
497 if (!parse_port_spec((*argv)[0], &remotes, "127.0.0.1")) break;
498 ++(*argv); --(*argc);
499 }
500 #endif
501
502 if (nfd < 1) nfd = 1;
503 if (npdu < 1) npdu = 1;
504 if (nthr < 1) nthr = 1;
505 }
506
507 /* Parse serial port config string and do all the ioctls to get it right. */
508
509 /* Called by: zxbusd_main */
serial_init(struct hi_thr * hit,struct hi_host_spec * hs)510 static struct hi_io* serial_init(struct hi_thr* hit, struct hi_host_spec* hs)
511 {
512 #ifdef ENA_SERIAL
513 struct hi_io* io;
514 char tty[256];
515 char sync = 'S', parity = 'N';
516 int fd, ret, baud = 9600, bits = 8, stop = 1, framesize = 1000;
517 ret = sscanf(hs->specstr, "dts:%255[^:]:%c-%d-%d-%d%c%d",
518 tty, &sync, &baud, &framesize, &bits, &parity, &stop);
519 if (ret < 4) {
520 fprintf(stderr, "You must supply serial port name and config, e.g. `dts:/dev/ttyS0:A-9600-8N1'. You gave(%s). You loose.\n", hs->specstr);
521 exit(3);
522 }
523 fd = open(tty, O_RDWR | O_NOCTTY | O_NDELAY);
524 if (fd == -1) {
525 ERR("open(%s): Error opening serial port: %d %s", tty, errno, STRERROR(errno));
526 exit(3);
527 }
528 if (fd >= shf->max_ios) {
529 ERR("serial: File descriptor limit(%d) exceeded fd=%d. Consider increasing the limit with -nfd flag, or figure out if there are any descriptor leaks.", shf->max_ios, fd);
530 close(fd);
531 return 0;
532 }
533 io = hit->shf->ios + fd;
534 io->qel.proto = hs->proto;
535 if (verbose)
536 log_port_info(fd, tty, "before");
537 if (set_baud_rate(fd, tty, baud) == -1)
538 exit(3);
539 if (set_frame_size(fd, tty, framesize) == -1)
540 exit(3);
541 if (verbose)
542 log_port_info(fd, tty, "after");
543 nonblock(fd);
544 LOCK(io->qel.mut, "serial_init");
545 hi_add_fd(hit, io, fd, HI_TCP_C);
546 UNLOCK(io->qel.mut, "serial_init");
547 return io;
548 #else
549 return 0;
550 #endif
551 }
552
553 /*() New born threads start here. hit is allocated from stack.
554 * In principle all threads are created equal and any one of
555 * then can act as the shuffler on its turn. */
556
557 /* Called by: */
thread_loop(void * _shf)558 void* thread_loop(void* _shf)
559 {
560 struct hi_thr hit;
561 struct hiios* shf = (struct hiios*)_shf;
562 hi_hit_init(&hit);
563 if (ak_buf_size)
564 ak_add_thread(ak_buf_size, 1); /* Add newly born thread */
565 hi_shuffle(&hit, shf); /* Never returns. */
566 return 0;
567 }
568
569 /* ============== M A I N ============== */
570
571 pthread_mutexattr_t MUTEXATTR_DECL;
572 extern int zxid_suppress_vpath_warning;
573
574 #ifndef zxbusd_main
575 #define zxbusd_main main
576 #endif
577
578 /* Called by: */
zxbusd_main(int argc,char ** argv,char ** env)579 int zxbusd_main(int argc, char** argv, char** env)
580 {
581 struct hi_thr hit;
582 hi_hit_init(&hit);
583 ak_init(*argv);
584 #ifdef MINGW
585 pthread_mutex_init(&dbilock, 0);
586 pthread_mutex_init(&shuff_mutex, 0);
587 pthread_mutex_init(&gethostbyname_mutex, 0);
588 {
589 WSADATA wsaDat;
590 WORD vers = MAKEWORD(2,2); /* or 2.0? */
591 ret = WSAStartup(vers, &wsaDat);
592 if (ret) {
593 ERR("WinSock DLL could not be initialized: %d", ret);
594 return -1;
595 }
596 }
597 #endif
598 #if !defined(MACOS) && !defined(MINGW)
599 # ifdef MUTEX_DEBUG
600 # ifndef PTHREAD_MUTEX_ERRORCHECK_NP
601 # define PTHREAD_MUTEX_ERRORCHECK_NP 2
602 # endif
603 if (pthread_mutexattr_init(MUTEXATTR)) NEVERNEVER("unable to initialize mutexattr %d",argc);
604 # ifndef __dietlibc__
605 if (pthread_mutexattr_settype(MUTEXATTR, PTHREAD_MUTEX_ERRORCHECK_NP))
606 NEVERNEVER("unable to set mutexattr %d",argc);
607 # endif
608 # endif
609 #endif
610 #ifdef COMPILED_DATE
611 int now = time(0);
612 if (COMPILED_DATE + TWO_MONTHS < now) { /* *** this logic needs refinement and error code of its own --Sampo */
613 if (COMPILED_DATE + THREE_MONTHS < now){
614 ERR("Evaluation copy expired. %d",0);
615 exit(4);
616 } else
617 ERR("Evaluation copy expired, in %d secs this program will stop working", COMPILED_DATE + THREE_MONTHS-now);
618 } else {
619 if (now + ONE_DAY < COMPILED_DATE){
620 ERR("Check for demo erroneus. Clock set too far in past? %d",0);
621 exit(4);
622 }
623 }
624 #endif
625
626 zxid_suppress_vpath_warning = 1;
627 zxbus_cf = zxid_new_conf_to_cf("PATH=" ZXBUS_PATH);
628 /*openlog("zxbusd", LOG_PID, LOG_LOCAL0); *** Do we want syslog logging? */
629 opt(&argc, &argv, &env);
630 zxbus_path = zxbus_cf->cpath;
631
632 /*if (stats_prefix) init_cmdline(argc, argv, env, stats_prefix);*/
633 CMDLINE("init");
634
635 if (pid_path) {
636 int len;
637 char buf[INTSTRLEN];
638 len = sprintf(buf, "%d", (int)getpid());
639 DD("pid_path=`%s'", pid_path);
640 if (write2_or_append_lock_c_path(pid_path,0,0,len,buf, "write pid", SEEK_SET, O_TRUNC) <= 0) {
641 ERR("Failed to write PID file(%s). Exiting. (Do not supply -pid if you do not want pid file.)", pid_path);
642 exit(1);
643 }
644 }
645
646 if (watchdog) {
647 #ifdef MINGW
648 ERR("Watch dog feature not supported on Windows.");
649 #else
650 int ret, watch_dog_iteration = 0;
651 while (1) {
652 ++watch_dog_iteration;
653 D("Watch dog loop %d", watch_dog_iteration);
654 switch (ret = fork()) {
655 case -1:
656 ERR("Watch dog %d: attempt to fork() real server failed: %d %s. Perhaps max number of processes has been reached or we are out of memory. Will try again in a sec. To stop a vicious cycle `kill -9 %d' to terminate this watch dog.", watch_dog_iteration, errno, STRERROR(errno), getpid());
657 break;
658 case 0: goto normal_child; /* Only way out of this loop */
659 default:
660 /* Reap the child */
661 switch (waitpid(ret, &ret, 0)) {
662 case -1:
663 ERR("Watch dog %d: attempt to waitpid() real server failed: %d %s. To stop a vicious cycle `kill -9 %d' to terminate this watch dog.", watch_dog_iteration, errno, STRERROR(errno), getpid());
664 break;
665 default:
666 ERR("Watch dog %d: Real server exited. Will restart in a sec. To stop a vicious cycle `kill -9 %d' to terminate this watch dog.", watch_dog_iteration, getpid());
667 }
668 }
669 sleep(1); /* avoid spinning tightly */
670 }
671 #endif
672 }
673
674 normal_child:
675 D("Real server pid %d", getpid());
676
677 if (kidpid_path) {
678 int len;
679 char buf[INTSTRLEN];
680 len = sprintf(buf, "%d", (int)getpid());
681 if (write2_or_append_lock_c_path(pid_path,0,0,len,buf,"write kidpid",SEEK_SET,O_TRUNC) <= 0) {
682 ERR("Failed to write kidpid file(%s). If you do not want kidpid file, do not supply -kidpid option. Continuing anyway.", pid_path);
683 }
684 }
685
686 #ifndef MINGW
687 if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) { /* Ignore SIGPIPE */
688 perror("signal ignore pipe");
689 exit(2);
690 }
691
692 /* Cause exit(3) to be called with the intent that any gcov profiling will get
693 * written to disk before we die. If zxbusd is not stopped with `kill -USR1' and you
694 * use plain kill instead, the profile will indicate many unexecuted (#####) lines. */
695 if (signal(SIGUSR1, exit) == SIG_ERR) {
696 perror("signal USR1 exit");
697 exit(2);
698 }
699 #endif
700
701 shuff = hi_new_shuffler(&hit, nfd, npdu, nch, nthr);
702 {
703 struct hi_io* io;
704 struct hi_host_spec* hs;
705 struct hi_host_spec* hs_next;
706
707 /* Prepare listeners first so we can then later connect to ourself. */
708 CMDLINE("listen");
709
710 for (hs = listen_ports; hs; hs = hs->next) {
711 io = hi_open_listener(shuff, hs, hs->proto);
712 if (!io)
713 break;
714 io->n = hs->conns;
715 hs->conns = io;
716 }
717
718 for (hs = remotes; hs; hs = hs_next) {
719 hs_next = hs->next;
720 hs->next = hi_prototab[hs->proto].specs;
721 hi_prototab[hs->proto].specs = hs;
722 if (hs->proto == HIPROTO_SMTP)
723 continue; /* SMTP connections are opened later, when actual data from SIS arrives. */
724
725 if (hs->sin.sin_family == (unsigned short int)0xfead)
726 io = serial_init(&hit, hs);
727 else
728 io = hi_open_tcp(&hit, hs, hs->proto);
729 if (!io)
730 break;
731 io->n = hs->conns;
732 hs->conns = io;
733 #ifdef ENA_S5066
734 switch (hs->proto) {
735 case S5066_SIS: /* *** Always bind as HMTP. Make configurable. */
736 sis_send_bind(&hit, io, SAP_ID_HMTP, 0, 0x0200); /* 0x0200 == nonarq, no repeats */
737 break;
738 case S5066_DTS:
739 ZMALLOC(io->ad.dts);
740 io->ad.dts->remote_station_addr[0] = 0x61; /* three nibbles long (padded with zeroes) */
741 io->ad.dts->remote_station_addr[1] = 0x23;
742 io->ad.dts->remote_station_addr[2] = 0x00;
743 io->ad.dts->remote_station_addr[3] = 0x00;
744 break;
745 }
746 #endif
747 }
748 }
749
750 if (snmp_port) {
751 #ifdef HAVE_NET_SNMP
752 initializeSNMPSubagent("open5066", SNMPLOGFILE);
753 /* *** we need to discover the SNMP socket somehow so we can insert it to
754 * our file descriptor table so it gets properly polled, etc. --Sampo */
755 #else
756 ERR("This binary was not compiled to support SNMP (%d). Continuing without.", snmp_port);
757 #endif
758 }
759
760 /* Drop privileges, if requested. */
761
762 if (drop_gid) if (setgid(drop_gid)) { perror("setgid"); exit(1); }
763 if (drop_uid) if (setuid(drop_uid)) { perror("setuid"); exit(1); }
764
765 CMDLINE("load_subs");
766 zxbus_load_subs(shuff);
767
768 hi_sanity_shf(255, shuff);
769
770 /* Unleash threads so that the listeners are served. */
771
772 CMDLINE("unleash");
773 {
774 int err;
775 pthread_t tid;
776 for (--nthr; nthr; --nthr)
777 if ((err = pthread_create(&tid, 0, thread_loop, shuff))) {
778 ERR("pthread_create() failed: %d (nthr=%d)", err, nthr);
779 exit(2);
780 }
781 }
782
783 hi_shuffle(&hit, shuff); /* main thread becomes one of the workers */
784 return 0; /* never really happens because hi_shuffle() never returns */
785 }
786
787 //char* assert_msg = "%s: Internal error caused an ASSERT to fire. Deliberately provoking a core dump.\nSorry for the inconvenience and thank you for your collaboration.\n";
788
789 /* EOF -- zxbusd.c */
790