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