1 /*
2  *  ser2net - A program for allowing telnet connection to serial ports
3  *  Copyright (C) 2001  Corey Minyard <minyard@acm.org>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19 
20 /* This is the entry point for the ser2net program.  It reads
21    parameters, initializes everything, then starts the select loop. */
22 
23 /* TODO
24  *
25  * Add some type of security
26  */
27 
28 #include <stdio.h>
29 #include <signal.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <sys/types.h>
34 #include <unistd.h>
35 #include <errno.h>
36 
37 #include "ser2net.h"
38 #include "readconfig.h"
39 #include "controller.h"
40 #include "utils.h"
41 #include "selector.h"
42 #include "dataxfer.h"
43 #include "locking.h"
44 #include "led.h"
45 
46 static char *config_file = "/usr/local/etc/ser2net.conf";
47 int config_port_from_cmdline = 0;
48 char *config_port = NULL; /* Can be set from readconfig, too. */
49 static char *pid_file = NULL;
50 static int detach = 1;
51 int ser2net_debug = 0;
52 int ser2net_debug_level = 0;
53 volatile int in_shutdown = 0;
54 #ifdef USE_UUCP_LOCKING
55 int uucp_locking_enabled = 1;
56 #endif
57 #ifdef USE_PTHREADS
58 int num_threads = 1;
59 struct thread_info {
60     pthread_t id;
61 };
62 struct thread_info *threads;
63 #endif
64 
65 
66 struct selector_s *ser2net_sel;
67 char *rfc2217_signature = "ser2net";
68 
69 static char *help_string =
70 "%s: Valid parameters are:\n"
71 "  -c <config file> - use a config file besides /usr/local/etc/ser2net.conf\n"
72 "  -C <config line> - Handle a single configuration line.  This may be\n"
73 "     specified multiple times for multiple lines.  This is just like a\n"
74 "     line in the config file.  This disables the default config file,\n"
75 "     you must specify a -c after the last -C to have it read a config\n"
76 "     file, too.\n"
77 "  -p <controller port> - Start a controller session on the given TCP port\n"
78 "  -P <file> - set location of pid file\n"
79 "  -n - Don't detach from the controlling terminal\n"
80 "  -d - Don't detach and send debug I/O to standard output\n"
81 "  -l - Increate the debugging level\n"
82 #ifdef USE_UUCP_LOCKING
83 "  -u - Disable UUCP locking\n"
84 #endif
85 #ifdef USE_PTHREADS
86 "  -t <num threads> - Use the given number of threads, default 1\n"
87 #endif
88 "  -b - unused (was Do CISCO IOS baud-rate negotiation, instead of RFC2217)\n"
89 "  -v - print the program's version and exit\n"
90 "  -s - specify a default signature for RFC2217 protocol\n";
91 
92 static void
reread_config_file(void)93 reread_config_file(void)
94 {
95     if (config_file) {
96 	char *prev_config_port = config_port;
97 	config_port = NULL;
98 	syslog(LOG_INFO, "Got SIGHUP, re-reading configuration");
99 	readconfig_init();
100 	readconfig(config_file);
101 	if (config_port_from_cmdline) {
102 	    /* Never override the config port from the command line. */
103 	    free(config_port);
104 	    config_port = prev_config_port;
105 	    goto config_port_unchanged;
106 	}
107 	if (config_port && prev_config_port
108 	    && (strcmp(config_port, prev_config_port) == 0)) {
109 	    free(prev_config_port);
110 	    goto config_port_unchanged;
111 	}
112 
113 	if (prev_config_port) {
114 	    controller_shutdown();
115 	    free(prev_config_port);
116 	}
117 
118 	if (config_port) {
119 	    int rv = controller_init(config_port);
120 	    if (rv == CONTROLLER_INVALID_TCP_SPEC)
121 		syslog(LOG_ERR, "Invalid control port specified: %s",
122 		       config_port);
123 	    else if (rv == CONTROLLER_OUT_OF_MEMORY)
124 		syslog(LOG_ERR, "Out of memory opening control port: %s",
125 		       config_port);
126 	    else if (rv == CONTROLLER_CANT_OPEN_PORT)
127 		syslog(LOG_ERR, "Can't open control port: %s",
128 		       config_port);
129 	    if (rv) {
130 		syslog(LOG_ERR, "Control port is disabled");
131 		free(config_port);
132 		config_port = NULL;
133 	    }
134 	}
135     }
136  config_port_unchanged:
137     return;
138 }
139 
140 static void
arg_error(char * name)141 arg_error(char *name)
142 {
143     fprintf(stderr, help_string, name);
144     exit(1);
145 }
146 
147 static void
make_pidfile(void)148 make_pidfile(void)
149 {
150     FILE *fpidfile;
151     if (!pid_file)
152 	return;
153     fpidfile = fopen(pid_file, "w");
154     if (!fpidfile) {
155 	syslog(LOG_WARNING,
156 	       "Error opening pidfile '%s': %m, pidfile not created",
157 	       pid_file);
158 	pid_file = NULL;
159 	return;
160     }
161     fprintf(fpidfile, "%d\n", getpid());
162     fclose(fpidfile);
163 }
164 
165 static int dummyrv; /* Used to ignore return values of read() and write(). */
166 
167 /* Used to reliably deliver signals to a thread.  This is a pipe that
168    we write to from a signal handler to make sure it wakes up. */
169 static int sig_fd_alert = -1;
170 static int sig_fd_watch = -1;
171 static volatile int reread_config = 0; /* Did I get a HUP signal? */
172 static volatile int term_prog = 0; /* Did I get an INT signal? */
173 
174 static void
sig_wake_selector(void)175 sig_wake_selector(void)
176 {
177     char dummy = 0;
178 
179     dummyrv = write(sig_fd_alert, &dummy, 1);
180 }
181 
182 static void
sighup_handler(int sig)183 sighup_handler(int sig)
184 {
185     reread_config = 1;
186     sig_wake_selector();
187 }
188 
189 static void
sigint_handler(int sig)190 sigint_handler(int sig)
191 {
192     term_prog = 1;
193     sig_wake_selector();
194 }
195 
196 #if USE_PTHREADS
197 DEFINE_LOCK_INIT(static, config_lock)
198 static int in_config_read = 0;
199 
200 DEFINE_LOCK_INIT(static, maint_lock)
201 
202 int ser2net_wake_sig = SIGUSR1;
203 void (*finish_shutdown)(void);
204 
205 void
start_maint_op(void)206 start_maint_op(void)
207 {
208     LOCK(maint_lock);
209 }
210 
211 void
end_maint_op(void)212 end_maint_op(void)
213 {
214     UNLOCK(maint_lock);
215 }
216 
217 static void *
config_reread_thread(void * dummy)218 config_reread_thread(void *dummy)
219 {
220     pthread_detach(pthread_self());
221     start_maint_op();
222     reread_config_file();
223     end_maint_op();
224     LOCK(config_lock);
225     in_config_read = 0;
226     UNLOCK(config_lock);
227     return NULL;
228 }
229 
230 static void
thread_reread_config_file(void)231 thread_reread_config_file(void)
232 {
233     int rv;
234     pthread_t thread;
235 
236     LOCK(config_lock);
237     if (in_config_read) {
238 	UNLOCK(config_lock);
239 	return;
240     }
241     in_config_read = 1;
242     UNLOCK(config_lock);
243 
244     rv = pthread_create(&thread, NULL, config_reread_thread, NULL);
245     if (rv) {
246 	syslog(LOG_ERR,
247 	       "Unable to start thread to reread config file: %s",
248 	       strerror(rv));
249 	LOCK(config_lock);
250 	in_config_read = 0;
251 	UNLOCK(config_lock);
252     }
253 }
254 
255 static void
wake_thread_sighandler(int sig)256 wake_thread_sighandler(int sig)
257 {
258     /* Nothing to do, sending the sig just wakes up select(). */
259 }
260 
261 static void
wake_thread_send_sig(long thread_id,void * cb_data)262 wake_thread_send_sig(long thread_id, void *cb_data)
263 {
264     pthread_t        *id = (void *) thread_id;
265 
266     pthread_kill(*id, ser2net_wake_sig);
267 }
268 
269 static void *
op_loop(void * dummy)270 op_loop(void *dummy)
271 {
272     pthread_t self = pthread_self();
273 
274     while (!in_shutdown)
275 	sel_select(ser2net_sel, wake_thread_send_sig, (long) &self, NULL, NULL);
276 
277     /* Join the threads only in the first thread.  You cannot join the
278        first thread.  Finish thw shutdown in the first thread. */
279     if (self == threads[0].id) {
280 	int i;
281 
282 	for (i = 0; i < num_threads; i++) {
283 	    if (threads[i].id == self)
284 		continue;
285 	    pthread_join(threads[i].id, NULL);
286 	}
287 	free(threads);
288 
289 	finish_shutdown();
290     }
291     return NULL;
292 }
293 
294 static void
start_threads(void)295 start_threads(void)
296 {
297     int i, rv;
298     struct sigaction act;
299 
300     act.sa_handler = wake_thread_sighandler;
301     sigemptyset(&act.sa_mask);
302     act.sa_flags = 0;
303     rv = sigaction(ser2net_wake_sig, &act, NULL);
304     if (rv) {
305 	syslog(LOG_ERR, "Unable to set sigaction: %s", strerror(errno));
306 	exit(1);
307     }
308 
309     threads = malloc(sizeof(*threads) * num_threads);
310     if (!threads) {
311 	syslog(LOG_ERR, "Unable to allocate thread info");
312 	exit(1);
313     }
314 
315     threads[0].id = pthread_self();
316 
317     for (i = 1; i < num_threads; i++) {
318 	rv = pthread_create(&threads[i].id, NULL, op_loop, NULL);
319 	if (rv) {
320 	    syslog(LOG_ERR, "Unable to start thread: %s", strerror(rv));
321 	    exit(1);
322 	}
323     }
324 }
325 
326 static void
stop_threads(void (* finish)(void))327 stop_threads(void (*finish)(void))
328 {
329     int i;
330     pthread_t self = pthread_self();
331 
332     in_shutdown = 1;
333     finish_shutdown = finish;
334     start_maint_op();
335     /* Make sure we aren't in a reconfig. */
336     end_maint_op();
337 
338     for (i = 0; i < num_threads; i++) {
339 	if (threads[i].id == self)
340 	    continue;
341 	pthread_kill(threads[i].id, ser2net_wake_sig);
342     }
343 }
344 
345 struct sel_lock_s
346 {
347     pthread_mutex_t lock;
348 };
349 
350 static sel_lock_t *
slock_alloc(void * cb_data)351 slock_alloc(void *cb_data)
352 {
353     sel_lock_t *l;
354 
355     l = malloc(sizeof(*l));
356     if (!l)
357 	return NULL;
358     pthread_mutex_init(&l->lock, NULL);
359     return l;
360 }
361 
362 static void
slock_free(sel_lock_t * l)363 slock_free(sel_lock_t *l)
364 {
365     pthread_mutex_destroy(&l->lock);
366     free(l);
367 }
368 
369 static void
slock_lock(sel_lock_t * l)370 slock_lock(sel_lock_t *l)
371 {
372     pthread_mutex_lock(&l->lock);
373 }
374 
375 static void
slock_unlock(sel_lock_t * l)376 slock_unlock(sel_lock_t *l)
377 {
378     pthread_mutex_unlock(&l->lock);
379 }
380 
381 #else
382 int ser2net_wake_sig = 0;
start_maint_op(void)383 void start_maint_op(void) { }
end_maint_op(void)384 void end_maint_op(void) { }
start_threads(void)385 static void start_threads(void) { }
stop_threads(void (* finish)(void))386 static void stop_threads(void (*finish)(void)) { finish(); }
387 #define slock_alloc NULL
388 #define slock_free NULL
389 #define slock_lock NULL
390 #define slock_unlock NULL
391 static void *
op_loop(void * dummy)392 op_loop(void *dummy)
393 {
394     sel_select_loop(ser2net_sel, NULL, 0, NULL);
395     return NULL;
396 }
397 #endif /* USE_PTHREADS */
398 
399 static void
finish_shutdown_cleanly(void)400 finish_shutdown_cleanly(void)
401 {
402     struct timeval tv;
403 
404     sel_clear_fd_handlers(ser2net_sel, sig_fd_watch);
405     free_rotators();
406     free_controllers();
407     shutdown_ports();
408     do {
409 	if (check_ports_shutdown())
410 	    break;
411 	tv.tv_sec = 1;
412 	tv.tv_usec = 0;
413 	sel_select(ser2net_sel, NULL, 0, NULL, &tv);
414     } while(1);
415 
416     sol_shutdown(); /* Free's the selector. */
417 
418     free_longstrs();
419     free_tracefiles();
420     free_rs485confs();
421 
422     if (pid_file)
423 	unlink(pid_file);
424 
425     exit(1);
426 }
427 
428 static void
shutdown_cleanly(void)429 shutdown_cleanly(void)
430 {
431     stop_threads(finish_shutdown_cleanly);
432 }
433 
434 static void
sig_fd_read_handler(int fd,void * cb_data)435 sig_fd_read_handler(int fd, void *cb_data)
436 {
437     char dummy[10];
438 
439     dummyrv = read(fd, dummy, sizeof(dummy));
440 
441     if (term_prog)
442 	shutdown_cleanly();
443 
444     if (reread_config && !in_shutdown) {
445 #if USE_PTHREADS
446 	thread_reread_config_file();
447 #else
448 	reread_config_file();
449 #endif
450     }
451 }
452 
453 static void
setup_signals(void)454 setup_signals(void)
455 {
456     struct sigaction act;
457     int              err;
458     int              pipefds[2];
459 
460     /* Ignore SIGPIPEs so they don't kill us. */
461     signal(SIGPIPE, SIG_IGN);
462 
463     err = pipe(pipefds);
464     if (err)
465 	goto out;
466 
467     sig_fd_alert = pipefds[1];
468     sig_fd_watch = pipefds[0];
469 
470     act.sa_handler = sighup_handler;
471     sigemptyset(&act.sa_mask);
472     act.sa_flags = SA_RESTART;
473     err = sigaction(SIGHUP, &act, NULL);
474     if (err)
475 	goto out;
476 
477     act.sa_handler = sigint_handler;
478     /* Only handle SIGINT once. */
479     act.sa_flags |= SA_RESETHAND;
480     err = sigaction(SIGINT, &act, NULL);
481     if (!err)
482 	err = sigaction(SIGQUIT, &act, NULL);
483     if (!err)
484 	err = sigaction(SIGTERM, &act, NULL);
485     if (err)
486 	goto out;
487 
488     sel_set_fd_handlers(ser2net_sel, sig_fd_watch, NULL, sig_fd_read_handler,
489 			NULL, NULL, NULL);
490     sel_set_fd_read_handler(ser2net_sel, sig_fd_watch, SEL_FD_HANDLER_ENABLED);
491 
492  out:
493     if (err) {
494 	fprintf(stderr, "Error setting up signals: %s\n", strerror(errno));
495 	exit(1);
496     }
497 }
498 
499 int
main(int argc,char * argv[])500 main(int argc, char *argv[])
501 {
502     int i;
503     int err;
504 #ifdef USE_PTHREADS
505     char *end;
506 #endif
507     char **config_lines;
508     int num_config_lines = 0;
509 
510     config_lines = malloc(sizeof(*config_lines));
511     if (!config_lines) {
512 	fprintf(stderr, "Out of memory\n");
513 	exit(1);
514     }
515     *config_lines = NULL;
516 
517     if (led_driver_init() < 0) {
518 	fprintf(stderr, "Error while initializing LED drivers\n");
519 	exit(1);
520     }
521 
522     for (i = 1; i < argc; i++) {
523 	if ((argv[i][0] != '-') || (strlen(argv[i]) != 2)) {
524 	    fprintf(stderr, "Invalid argument: '%s'\n", argv[i]);
525 	    arg_error(argv[0]);
526 	}
527 
528 	switch (argv[i][1]) {
529 	case 'n':
530 	    detach = 0;
531 	    break;
532 
533 	case 'd':
534 	    detach = 0;
535 	    ser2net_debug = 1;
536 	    break;
537 
538 	case 'l':
539 	    ser2net_debug_level++;
540 	    break;
541 
542 	case 'b':
543 	    break;
544 
545 	case 'C':
546 	    /* Get a config line. */
547 	    i++;
548 	    if (i == argc) {
549 		fprintf(stderr, "No config line specified with -C\n");
550 		arg_error(argv[0]);
551 	    }
552 	    num_config_lines++;
553 	    config_lines = realloc(config_lines, sizeof(*config_lines) *
554 				   (num_config_lines + 1));
555 	    if (!config_lines) {
556 		fprintf(stderr, "Out of memory handling config line\n");
557 		exit(1);
558 	    }
559 	    config_lines[num_config_lines - 1] = argv[i];
560 	    config_file = NULL;
561 	    break;
562 
563 	case 'c':
564 	    /* Get a config file. */
565 	    i++;
566 	    if (i == argc) {
567 		fprintf(stderr, "No config file specified with -c\n");
568 		arg_error(argv[0]);
569 	    }
570 	    config_file = argv[i];
571 	    break;
572 
573 	case 'p':
574 	    /* Get the control port. */
575 	    i++;
576 	    if (i == argc) {
577 		fprintf(stderr, "No control port specified with -p\n");
578 		arg_error(argv[0]);
579 	    }
580 	    config_port = strdup(argv[i]);
581 	    if (!config_port) {
582 		fprintf(stderr, "Could not allocate memory for -p\n");
583 		exit(1);
584 	    }
585 	    config_port_from_cmdline = 1;
586 	    break;
587 
588 	case 'P':
589 	    i++;
590 	    if (i == argc) {
591 		fprintf(stderr, "No pid file specified with -P\n");
592 		arg_error(argv[0]);
593 	    }
594 	    pid_file = argv[i];
595 	    break;
596 
597 #ifdef USE_UUCP_LOCKING
598 	case 'u':
599 	    uucp_locking_enabled = 0;
600 	    break;
601 #endif
602 
603 	case 'v':
604 	    printf("%s version %s\n", argv[0], VERSION);
605 	    exit(0);
606 
607 	case 's':
608             i++;
609             if (i == argc) {
610 	        fprintf(stderr, "No signature specified\n");
611 		exit(1);
612             }
613             rfc2217_signature = argv[i];
614             break;
615 
616 #ifdef USE_PTHREADS
617 	case 't':
618             i++;
619             if (i == argc) {
620 	        fprintf(stderr, "No thread count specified\n");
621 		exit(1);
622             }
623 	    num_threads = strtoul(argv[i], &end, 10);
624 	    if (end == argv[i] || *end != '\0') {
625 	        fprintf(stderr, "Invalid thread count specified: %s\n",
626 			argv[i]);
627 		exit(1);
628 	    }
629             break;
630 #endif
631 
632 	default:
633 	    fprintf(stderr, "Invalid option: '%s'\n", argv[i]);
634 	    arg_error(argv[0]);
635 	}
636     }
637 
638 #ifdef USE_PTHREADS
639     if (num_threads > 1)
640 	err = sel_alloc_selector_thread(&ser2net_sel, ser2net_wake_sig,
641 					slock_alloc, slock_free,
642 					slock_lock, slock_unlock, NULL);
643     else
644 #endif
645 	err = sel_alloc_selector_nothread(&ser2net_sel);
646 
647     if (err) {
648 	fprintf(stderr,
649 		"Could not initialize ser2net selector: '%s'\n",
650 		strerror(err));
651 	exit(1);
652     }
653 
654     setup_signals();
655 
656     err = sol_init();
657     if (err) {
658 	fprintf(stderr,
659 		"Could not initialize IPMI SOL: '%s'\n",
660 		strerror(-err));
661 	exit(1);
662     }
663 
664     if (ser2net_debug && !detach)
665 	openlog("ser2net", LOG_PID | LOG_CONS | LOG_PERROR, LOG_DAEMON);
666 
667     readconfig_init();
668     for (i = 0; i < num_config_lines; i++)
669 	handle_config_line(config_lines[i], strlen(config_lines[i]));
670     free(config_lines);
671     if (config_file) {
672 	if (readconfig(config_file) == -1)
673 	    exit(1);
674     }
675 
676     if (config_port != NULL) {
677 	int rv;
678 	rv = controller_init(config_port);
679 	if (rv == CONTROLLER_INVALID_TCP_SPEC) {
680 	    fprintf(stderr, "Invalid control port specified: %s\n",
681 		    config_port);
682 	    arg_error(argv[0]);
683 	}
684 	if (rv == CONTROLLER_CANT_OPEN_PORT) {
685 	    fprintf(stderr, "Unable to open control port, see syslog: %s\n",
686 		    config_port);
687 	    exit(1);
688 	}
689     }
690 
691     if (detach) {
692 	int pid;
693 
694 	/* Detach from the calling terminal. */
695 	openlog("ser2net", LOG_PID | LOG_CONS, LOG_DAEMON);
696 	syslog(LOG_NOTICE, "ser2net startup");
697 	if ((pid = fork()) > 0) {
698 	    exit(0);
699 	} else if (pid < 0) {
700 	    syslog(LOG_ERR, "Error forking first fork: %s", strerror(errno));
701 	    exit(1);
702 	} else {
703 	    /* setsid() is necessary if we really want to demonize */
704 	    setsid();
705 	    /* Second fork to really deamonize me. */
706 	    if ((pid = fork()) > 0) {
707 		exit(0);
708 	    } else if (pid < 0) {
709 		syslog(LOG_ERR, "Error forking second fork: %s",
710 		       strerror(errno));
711 		exit(1);
712 	    }
713 	}
714 
715 	/* Close all my standard I/O. */
716 	if (chdir("/") < 0) {
717 	    syslog(LOG_ERR, "unable to chdir to '/': %s", strerror(errno));
718 	    exit(1);
719 	}
720 	close(0);
721 	close(1);
722 	close(2);
723     }
724 
725     /* write pid file */
726     make_pidfile();
727 
728     start_threads();
729     op_loop(NULL);
730 
731     sel_free_selector(ser2net_sel);
732 
733     return 0;
734 }
735