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