1 /*****************************************************************************
2  *  Written by Chris Dunlap <cdunlap@llnl.gov>.
3  *  Copyright (C) 2007-2018 Lawrence Livermore National Security, LLC.
4  *  Copyright (C) 2001-2007 The Regents of the University of California.
5  *  UCRL-CODE-2002-009.
6  *
7  *  This file is part of ConMan: The Console Manager.
8  *  For details, see <https://dun.github.io/conman/>.
9  *
10  *  ConMan is free software: you can redistribute it and/or modify it under
11  *  the terms of the GNU General Public License as published by the Free
12  *  Software Foundation, either version 3 of the License, or (at your option)
13  *  any later version.
14  *
15  *  ConMan is distributed in the hope that it will be useful, but WITHOUT
16  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18  *  for more details.
19  *
20  *  You should have received a copy of the GNU General Public License along
21  *  with ConMan.  If not, see <http://www.gnu.org/licenses/>.
22  *****************************************************************************/
23 
24 
25 #if HAVE_CONFIG_H
26 #  include <config.h>
27 #endif /* HAVE_CONFIG_H */
28 
29 #include <assert.h>
30 #include <errno.h>
31 #include <fcntl.h>
32 #include <limits.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/stat.h>
37 #include <termios.h>
38 #include <unistd.h>
39 #include "common.h"
40 #include "list.h"
41 #include "log.h"
42 #include "server.h"
43 #include "tpoll.h"
44 #include "util-file.h"
45 #include "util-str.h"
46 
47 extern tpoll_t tp_global;               /* defined in server.c */
48 
49 
50 typedef struct bps_tag {
51     speed_t bps;
52     int     val;
53 } bps_tag_t;
54 
55 
56 static bps_tag_t bps_table[] = {        /* values are in increasing order */
57     {B50,     50},
58     {B75,     75},
59     {B110,    110},
60     {B134,    134},
61     {B150,    150},
62     {B200,    200},
63     {B300,    300},
64     {B600,    600},
65     {B1200,   1200},
66     {B1800,   1800},
67     {B2400,   2400},
68     {B4800,   4800},
69     {B9600,   9600},
70     {B19200,  19200},
71     {B38400,  38400},                   /* end of the line for POSIX.1 bps's */
72 #ifdef B57600
73     {B57600,  57600},
74 #endif /* B57600 */
75 #ifdef B115200
76     {B115200, 115200},
77 #endif /* B115200 */
78 #ifdef B230400
79     {B230400, 230400},
80 #endif /* B230400 */
81 #ifdef B460800
82     {B460800, 460800},
83 #endif /* B460800 */
84     {0,       0}                        /* sentinel denotes end of array */
85 };
86 
87 
88 static speed_t int_to_bps(int val);
89 #ifndef NDEBUG
90 static int bps_to_int(speed_t bps);
91 static const char * parity_to_str(int parity);
92 #endif /* !NDEBUG */
93 
94 
is_serial_dev(const char * dev,const char * cwd,char ** path_ref)95 int is_serial_dev(const char *dev, const char *cwd, char **path_ref)
96 {
97     char         buf[PATH_MAX];
98     int          n;
99     struct stat  st;
100 
101     assert(dev != NULL);
102 
103     if ((dev[0] != '/') && (cwd != NULL)) {
104         n = snprintf(buf, sizeof(buf), "%s/%s", cwd, dev);
105         if ((n < 0) || ((size_t) n >= sizeof(buf))) {
106             return(0);
107         }
108         dev = buf;
109     }
110     if (stat(dev, &st) < 0) {
111         return(0);
112     }
113     if (!S_ISCHR(st.st_mode)) {
114         return(0);
115     }
116     if (path_ref) {
117         *path_ref = create_string(dev);
118     }
119     return(1);
120 }
121 
122 
parse_serial_opts(seropt_t * opts,const char * str,char * errbuf,int errlen)123 int parse_serial_opts(
124     seropt_t *opts, const char *str, char *errbuf, int errlen)
125 {
126 /*  Parses 'str' for serial device options 'opts'.
127  *    The 'opts' struct should be initialized to a default value.
128  *    The 'str' string is of the form "<bps>,<databits><parity><stopbits>".
129  *  Returns 0 and updates the 'opts' struct on success; o/w, returns -1
130  *    (writing an error message into 'errbuf' if defined).
131  */
132     int n;
133     seropt_t optsTmp;
134     int bpsTmp;
135     char parityTmp;
136 
137     assert(opts != NULL);
138 
139     /*  By setting the tmp opts to the 'opts' that are passed in,
140      *    we establish defaults for any values that are not changed by 'str'.
141      */
142     optsTmp = *opts;
143 
144     if ((str == NULL) || str[0] == '\0') {
145         if ((errbuf != NULL) && (errlen > 0))
146             snprintf(errbuf, errlen,
147                 "encountered empty options string");
148         return(-1);
149     }
150 
151     n = sscanf(str, "%d,%d%c%d", &bpsTmp, &optsTmp.databits,
152         &parityTmp, &optsTmp.stopbits);
153 
154     if (n >= 1) {
155         optsTmp.bps = int_to_bps(bpsTmp);
156         if (optsTmp.bps <= 0) {
157             if ((errbuf != NULL) && (errlen > 0))
158                 snprintf(errbuf, errlen,
159                     "expected INTEGER >0 for bps setting");
160             return(-1);
161         }
162     }
163     if (n >= 2) {
164         if ((optsTmp.databits < 5) || (optsTmp.databits > 8)) {
165             if ((errbuf != NULL) && (errlen > 0))
166                 snprintf(errbuf, errlen,
167                     "expected INTEGER 5-8 for databits setting");
168             return(-1);
169         }
170     }
171     if (n >= 3) {
172         switch(parityTmp) {
173         case 'N':
174         case 'n':
175             optsTmp.parity = 0;
176             break;
177         case 'O':
178         case 'o':
179             optsTmp.parity = 1;
180             break;
181         case 'E':
182         case 'e':
183             optsTmp.parity = 2;
184             break;
185         default:
186             if ((errbuf != NULL) && (errlen > 0))
187                 snprintf(errbuf, errlen,
188                     "expected (N|O|E) for parity setting");
189             return(-1);
190             break;
191         }
192     }
193     if (n >= 4) {
194         if ((optsTmp.stopbits < 1) || (optsTmp.stopbits > 2)) {
195             if ((errbuf != NULL) && (errlen > 0))
196                 snprintf(errbuf, errlen,
197                     "expected INTEGER 1-2 for stopbits setting");
198             return(-1);
199         }
200     }
201 
202     *opts = optsTmp;
203     return(0);
204 }
205 
206 
int_to_bps(int val)207 static speed_t int_to_bps(int val)
208 {
209 /*  Converts a numeric value 'val' into a bps speed_t,
210  *    rounding down to the next bps value if necessary.
211  */
212     bps_tag_t *tag;
213     speed_t bps = 0;
214 
215     for (tag=bps_table; tag->val > 0; tag++) {
216         if (tag->val <= val)
217             bps = tag->bps;
218         else
219             break;
220     }
221     return(bps);
222 }
223 
224 
225 #ifndef NDEBUG
bps_to_int(speed_t bps)226 static int bps_to_int(speed_t bps)
227 {
228 /*  Converts a 'bps' speed_t into its numeric value.
229  *  Returns 0 if 'bps' does not correspond to any values in the table.
230  */
231     bps_tag_t *tag;
232 
233     for (tag=bps_table; tag->val > 0; tag++) {
234         if (tag->bps == bps)
235             return(tag->val);
236     }
237     return(0);
238 }
239 #endif /* !NDEBUG */
240 
241 
242 #ifndef NDEBUG
parity_to_str(int parity)243 static const char * parity_to_str(int parity)
244 {
245 /*  Returns a constant string denoting the specified 'parity' value.
246  */
247     if (parity == 1)
248         return("O");
249     else if (parity == 2)
250         return("E");
251     else /* (parity == 0) */
252         return("N");
253 }
254 #endif /* !NDEBUG */
255 
256 
set_serial_opts(struct termios * tty,obj_t * serial,seropt_t * opts)257 void set_serial_opts(struct termios *tty, obj_t *serial, seropt_t *opts)
258 {
259 /*  Sets serial device options specified by 'opts' for the
260  *   'tty' terminal settings associated with the 'serial' object.
261  *  Updates the 'tty' struct as appropriate.
262  */
263     assert(tty != NULL);
264     assert(serial != NULL);
265     assert(is_serial_obj(serial));
266     assert(opts != NULL);
267     assert(opts->bps > 0);
268     assert((opts->databits >= 5) && (opts->databits <= 8));
269     assert((opts->parity >= 0) && (opts->parity <= 2));
270     assert((opts->stopbits >= 1) && (opts->stopbits <= 2));
271 
272     DPRINTF((10, "Setting [%s] dev=%s to %d,%d%s%d.\n",
273         serial->name, serial->aux.serial.dev, bps_to_int(opts->bps),
274         opts->databits, parity_to_str(opts->parity), opts->stopbits));
275 
276     if (cfsetispeed(tty, opts->bps) < 0)
277         log_err(errno, "Unable to set [%s] input baud rate to %d",
278             serial->name, opts->bps);
279     if (cfsetospeed(tty, opts->bps) < 0)
280         log_err(errno, "Unable to set [%s] output baud rate to %d",
281             serial->name, opts->bps);
282 
283     tty->c_cflag &= ~CSIZE;
284     if (opts->databits == 5) {
285         tty->c_cflag |= CS5;
286     }
287     else if (opts->databits == 6) {
288         tty->c_cflag |= CS6;
289     }
290     else if (opts->databits == 7) {
291         tty->c_cflag |= CS7;
292     }
293     else /* (opts->databits == 8) */ {  /* safe default in case value is bad */
294         tty->c_cflag |= CS8;
295     }
296 
297     if (opts->parity == 1) {
298         tty->c_cflag |= (PARENB | PARODD);
299     }
300     else if (opts->parity == 2) {
301         tty->c_cflag |= PARENB;
302         tty->c_cflag &= ~PARODD;
303     }
304     else /* (opts->parity == 0) */ {    /* safe default in case value is bad */
305         tty->c_cflag &= ~(PARENB | PARODD);
306     }
307 
308     if (opts->stopbits == 2) {
309         tty->c_cflag |= CSTOPB;
310     }
311     else /* (opts->stopbits == 1) */ {  /* safe default in case value is bad */
312         tty->c_cflag &= ~CSTOPB;
313     }
314 
315     return;
316 }
317 
318 
create_serial_obj(server_conf_t * conf,char * name,char * dev,seropt_t * opts,char * errbuf,int errlen)319 obj_t * create_serial_obj(server_conf_t *conf, char *name,
320     char *dev, seropt_t *opts, char *errbuf, int errlen)
321 {
322 /*  Creates a new serial device object and adds it to the master objs list.
323  *    Note: the console is open and set for non-blocking I/O.
324  *  Returns the new object, or NULL on error.
325  */
326     ListIterator i;
327     obj_t *serial;
328 
329     assert(conf != NULL);
330     assert((name != NULL) && (name[0] != '\0'));
331     assert((dev != NULL) && (dev[0] != '\0'));
332     assert(opts != NULL);
333 
334     /*  Check for duplicate console and device names.
335      *  While the write-lock will protect against two separate daemons
336      *    using the same device, it will not protect against two console
337      *    objects within the same daemon process using the same device.
338      *    So that check is performed here.
339      */
340     i = list_iterator_create(conf->objs);
341     while ((serial = list_next(i))) {
342         if (is_console_obj(serial) && !strcmp(serial->name, name)) {
343             if ((errbuf != NULL) && (errlen > 0)) {
344                 snprintf(errbuf, errlen,
345                     "console [%s] specifies duplicate console name", name);
346             }
347             break;
348         }
349         if (is_serial_obj(serial) && !strcmp(serial->aux.serial.dev, dev)) {
350             if ((errbuf != NULL) && (errlen > 0)) {
351                 snprintf(errbuf, errlen,
352                     "console [%s] specifies duplicate device \"%s\"",
353                     name, dev);
354             }
355             break;
356         }
357     }
358     list_iterator_destroy(i);
359     if (serial != NULL) {
360         return(NULL);
361     }
362     serial = create_obj(conf, name, -1, CONMAN_OBJ_SERIAL);
363     serial->aux.serial.dev = create_string(dev);
364     serial->aux.serial.opts = *opts;
365     serial->aux.serial.logfile = NULL;
366     /*
367      *  Add obj to the master conf->objs list.
368      */
369     list_append(conf->objs, serial);
370 
371     return(serial);
372 }
373 
374 
open_serial_obj(obj_t * serial)375 int open_serial_obj(obj_t *serial)
376 {
377 /*  (Re)opens the specified 'serial' obj.
378  *  Returns 0 if the serial console is successfully opened; o/w, returns -1.
379  *
380  *  FIXME: Check to see if "downed" serial consoles are ever resurrected.
381  */
382     int fd;
383     int flags;
384     struct termios tty;
385 
386     assert(serial != NULL);
387     assert(is_serial_obj(serial));
388 
389     if (serial->fd >= 0) {
390         write_notify_msg(serial, LOG_INFO,
391             "Console [%s] disconnected from \"%s\"",
392             serial->name, serial->aux.serial.dev);
393         tpoll_clear(tp_global, serial->fd, POLLIN | POLLOUT);
394         set_tty_mode(&serial->aux.serial.tty, serial->fd);
395         if (close(serial->fd) < 0)      /* log err and continue */
396             log_msg(LOG_WARNING, "Unable to close [%s] device \"%s\": %s",
397                 serial->name, serial->aux.serial.dev, strerror(errno));
398         serial->fd = -1;
399     }
400     flags = O_RDWR | O_NONBLOCK | O_NOCTTY;
401     if ((fd = open(serial->aux.serial.dev, flags)) < 0) {
402         log_msg(LOG_WARNING, "Unable to open [%s] device \"%s\": %s",
403             serial->name, serial->aux.serial.dev, strerror(errno));
404         goto err;
405     }
406     if (get_write_lock(fd) < 0) {
407         log_msg(LOG_WARNING, "Unable to lock [%s] device \"%s\"",
408             serial->name, serial->aux.serial.dev);
409         goto err;
410     }
411     if (!isatty(fd)) {
412         log_msg(LOG_WARNING, "[%s] device \"%s\" not a terminal",
413             serial->name, serial->aux.serial.dev);
414         goto err;
415     }
416     /*  According to the UNIX Programming FAQ v1.37
417      *    <http://www.faqs.org/faqs/unix-faq/programmer/faq/>
418      *    (Section 3.6: How to Handle a Serial Port or Modem),
419      *    systems seem to differ as to whether a nonblocking
420      *    open on a tty will affect subsequent read()s.
421      *    Play it safe and be explicit!
422      */
423     set_fd_nonblocking(fd);
424     set_fd_closed_on_exec(fd);
425     /*
426      *  Note that while the initial state of the console dev's termios
427      *    are saved, the 'opts' settings are not.  This is because the
428      *    settings do not change until the obj is destroyed, at which time
429      *    the termios is reverted back to its initial state.
430      *
431      *  FIXME: Re-evaluate this thinking since a SIGHUP should attempt
432      *         to resurrect "downed" serial objs.
433      */
434     get_tty_mode(&serial->aux.serial.tty, fd);
435     get_tty_raw(&tty, fd);
436     set_serial_opts(&tty, serial, &serial->aux.serial.opts);
437     set_tty_mode(&tty, fd);
438     serial->fd = fd;
439     serial->gotEOF = 0;
440     tpoll_set(tp_global, serial->fd, POLLIN);
441     /*
442      *  Success!
443      */
444     write_notify_msg(serial, LOG_INFO, "Console [%s] connected to \"%s\"",
445         serial->name, serial->aux.serial.dev);
446     DPRINTF((9, "Opened [%s] serial: fd=%d dev=%s bps=%d.\n",
447         serial->name, serial->fd, serial->aux.serial.dev,
448         bps_to_int(serial->aux.serial.opts.bps)));
449     return(0);
450 
451 err:
452     if (fd >= 0) {
453         (void) close(fd);
454     }
455     return(-1);
456 }
457