├── compat ├── stdlib.h ├── unistd.h ├── strtonum.c ├── closefrom.c ├── vis.h └── vis.c ├── Makefile ├── LICENSE ├── README.md ├── cu.h ├── error.c ├── CMakeLists.txt ├── input.c ├── xmodem.c ├── cu.1 ├── command.c └── cu.c /compat/stdlib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * stdlib.h compatibility shim 3 | * Public domain 4 | */ 5 | 6 | #include_next 7 | 8 | #ifndef CUCOMPAT_STDLIB_H 9 | long long strtonum(const char *nptr, long long minval, 10 | long long maxval, const char **errstr); 11 | #endif 12 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # $OpenBSD: Makefile,v 1.3 2012/07/10 11:42:02 nicm Exp $ 2 | 3 | PROG= cu 4 | SRCS= cu.c command.c error.c input.c xmodem.c 5 | 6 | CDIAGFLAGS+= -Wall -W -Wno-unused-parameter 7 | 8 | LDADD= -levent 9 | DPADD= ${LIBEVENT} 10 | 11 | .include 12 | -------------------------------------------------------------------------------- /compat/unistd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * unistd.h compatibility shim 3 | * Public domain 4 | */ 5 | 6 | #include_next 7 | 8 | #ifndef CUCOMPAT_UNISTD_H 9 | #define CUCOMPAT_UNISTD_H 10 | 11 | #ifndef HAVE_CLOSEFROM 12 | void closefrom(int); 13 | #endif 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Permission to use, copy, modify, and distribute this software for any 2 | purpose with or without fee is hereby granted, provided that the above 3 | copyright notice and this permission notice appear in all copies. 4 | 5 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 6 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 7 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 8 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 9 | WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 10 | IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 11 | OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # opencu - serial terminal emulator 2 | 3 | This is a port of OpenBSD's serial terminal emulator [cu(1)](https://man.openbsd.org/cu.1) to Linux. 4 | 5 | ## Dependencies 6 | 7 | Portable [cu(1)](https://man.openbsd.org/cu.1) is built using cmake. It requires a working C compiler, standard library and headers and libevent. 8 | 9 | ## Building from source 10 | 11 | ``` 12 | mkdir build && cd build 13 | cmake -DCMAKE_BUILD_TYPE=Release .. 14 | make 15 | ``` 16 | 17 | ## Binary Packages 18 | 19 | Pre-built binary packages for opencu are available in [Debian](https://tracker.debian.org/pkg/opencu), [Ubuntu](https://launchpad.net/ubuntu/+source/opencu) 20 | and the [Arch User Repository (AUR)](https://aur.archlinux.org/packages/opencu). It is our hope that packagers take interest and help adapt opencu to 21 | more distributions. 22 | 23 | ## Usage 24 | 25 | To connect to the serial port at `/dev/ttyUSB0` with a baud rate of 115200 baud: 26 | 27 | ``` 28 | cu -l /dev/ttyUSB0 -s 115200 29 | ``` 30 | 31 | To close the connection and exit cu, enter `~.`. You can use `~?` to print a list of all supported commands. 32 | -------------------------------------------------------------------------------- /cu.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: cu.h,v 1.9 2019/03/22 07:03:23 nicm Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2012 Nicholas Marriott 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef CU_H 20 | #define CU_H 21 | 22 | /* command.c */ 23 | void do_command(char); 24 | 25 | /* cu.c */ 26 | extern int escape_char; 27 | extern int restricted; 28 | extern FILE *record_file; 29 | extern struct termios saved_tio; 30 | extern int line_fd; 31 | extern struct bufferevent *line_ev; 32 | void set_blocking(int, int); 33 | int set_line(int); 34 | void set_termios(void); 35 | void restore_termios(void); 36 | char *tilde_expand(const char *); 37 | 38 | /* input.c */ 39 | const char *get_input(const char *); 40 | 41 | /* error.c */ 42 | void cu_warn(const char *, ...) 43 | __attribute__ ((format (printf, 1, 2))); 44 | void cu_warnx(const char *, ...) 45 | __attribute__ ((format (printf, 1, 2))); 46 | void cu_err(int, const char *, ...) 47 | __attribute__ ((format (printf, 2, 3))); 48 | void cu_errx(int, const char *, ...) 49 | __attribute__ ((format (printf, 2, 3))); 50 | 51 | /* xmodem.c */ 52 | void xmodem_send(const char *); 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /compat/strtonum.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: strtonum.c,v 1.8 2015/09/13 08:31:48 guenther Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2004 Ted Unangst and Todd Miller 5 | * All rights reserved. 6 | * 7 | * Permission to use, copy, modify, and distribute this software for any 8 | * purpose with or without fee is hereby granted, provided that the above 9 | * copyright notice and this permission notice appear in all copies. 10 | * 11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #define INVALID 1 25 | #define TOOSMALL 2 26 | #define TOOLARGE 3 27 | 28 | long long 29 | strtonum(const char *numstr, long long minval, long long maxval, 30 | const char **errstrp) 31 | { 32 | long long ll = 0; 33 | int error = 0; 34 | char *ep; 35 | struct errval { 36 | const char *errstr; 37 | int err; 38 | } ev[4] = { 39 | { NULL, 0 }, 40 | { "invalid", EINVAL }, 41 | { "too small", ERANGE }, 42 | { "too large", ERANGE }, 43 | }; 44 | 45 | ev[0].err = errno; 46 | errno = 0; 47 | if (minval > maxval) { 48 | error = INVALID; 49 | } else { 50 | ll = strtoll(numstr, &ep, 10); 51 | if (numstr == ep || *ep != '\0') 52 | error = INVALID; 53 | else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) 54 | error = TOOSMALL; 55 | else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) 56 | error = TOOLARGE; 57 | } 58 | if (errstrp != NULL) 59 | *errstrp = ev[error].errstr; 60 | errno = ev[error].err; 61 | if (error) 62 | ll = 0; 63 | 64 | return (ll); 65 | } 66 | -------------------------------------------------------------------------------- /compat/closefrom.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004-2005, 2007, 2010, 2012-2015, 2017-2022 3 | * Todd C. Miller 4 | * 5 | * Permission to use, copy, modify, and distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #ifdef HAVE_LINUX_CLOSE_RANGE_H 19 | #include 20 | #endif 21 | #include 22 | #include 23 | #include "unistd.h" 24 | 25 | #ifndef OPEN_MAX 26 | # define OPEN_MAX 256 27 | #endif 28 | 29 | #if defined(HAVE_CLOSE_RANGE) 30 | void 31 | closefrom(int lowfd) 32 | { 33 | close_range(lowfd, ~0U, 0); 34 | } 35 | #else 36 | /* 37 | * Close all file descriptors greater than or equal to lowfd. 38 | * This is the expensive (fallback) method. 39 | */ 40 | void 41 | closefrom(int lowfd) 42 | { 43 | long fd, maxfd; 44 | 45 | /* 46 | * Fall back on sysconf(_SC_OPEN_MAX). This is equivalent to 47 | * checking the RLIMIT_NOFILE soft limit. It is possible for 48 | * there to be open file descriptors past this limit but there's 49 | * not much we can do about that since the hard limit may be 50 | * RLIM_INFINITY (LLONG_MAX or ULLONG_MAX on modern systems). 51 | */ 52 | maxfd = sysconf(_SC_OPEN_MAX); 53 | if (maxfd < OPEN_MAX) 54 | maxfd = OPEN_MAX; 55 | 56 | /* Make sure we didn't get RLIM_INFINITY as the upper limit. */ 57 | if (maxfd > INT_MAX) 58 | maxfd = INT_MAX; 59 | 60 | for (fd = lowfd; fd < maxfd; fd++) { 61 | (void)close((int)fd); 62 | } 63 | } 64 | #endif 65 | -------------------------------------------------------------------------------- /error.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: error.c,v 1.1 2012/07/10 10:28:05 nicm Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2012 Nicholas Marriott 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "cu.h" 29 | 30 | /* 31 | * Once we've configured termios, we need to use \r\n to end lines, so use our 32 | * own versions of warn/warnx/err/errx. 33 | */ 34 | 35 | extern char *__progname; 36 | 37 | void 38 | cu_err(int eval, const char *fmt, ...) 39 | { 40 | va_list ap; 41 | 42 | restore_termios(); 43 | 44 | va_start(ap, fmt); 45 | verr(eval, fmt, ap); 46 | } 47 | 48 | void 49 | cu_errx(int eval, const char *fmt, ...) 50 | { 51 | va_list ap; 52 | 53 | restore_termios(); 54 | 55 | va_start(ap, fmt); 56 | verrx(eval, fmt, ap); 57 | } 58 | 59 | void 60 | cu_warn(const char *fmt, ...) 61 | { 62 | va_list ap; 63 | 64 | fprintf(stderr, "%s: ", __progname); 65 | 66 | va_start(ap, fmt); 67 | vfprintf(stderr, fmt, ap); 68 | va_end(ap); 69 | 70 | fprintf(stderr, ": %s\r\n", strerror(errno)); 71 | } 72 | 73 | void 74 | cu_warnx(const char *fmt, ...) 75 | { 76 | va_list ap; 77 | 78 | fprintf(stderr, "%s: ", __progname); 79 | 80 | va_start(ap, fmt); 81 | vfprintf(stderr, fmt, ap); 82 | va_end(ap); 83 | 84 | fprintf(stderr, "\r\n"); 85 | } 86 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2022 Tobias Heider 2 | # 3 | # Permission to use, copy, modify, and distribute this software for any 4 | # purpose with or without fee is hereby granted, provided that the above 5 | # copyright notice and this permission notice appear in all copies. 6 | # 7 | # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | 15 | cmake_minimum_required(VERSION 3.12) 16 | 17 | include(CheckFunctionExists) 18 | include(CheckIncludeFiles) 19 | 20 | project(cu) 21 | 22 | add_definitions(-D_GNU_SOURCE) 23 | 24 | check_function_exists(closefrom HAVE_CLOSEFROM) 25 | if(HAVE_CLOSEFROM) 26 | add_definitions(-DHAVE_CLOSEFROM) 27 | endif() 28 | check_include_files("linux/close_range.h" HAVE_LINUX_CLOSE_RANGE_H) 29 | if(HAVE_LINUX_CLOSE_RANGE_H) 30 | add_definitions(-DHAVE_LINUX_CLOSE_RANGE_H) 31 | endif() 32 | check_function_exists(close_range HAVE_CLOSE_RANGE) 33 | if(HAVE_CLOSE_RANGE) 34 | add_definitions(-DHAVE_CLOSE_RANGE) 35 | endif() 36 | 37 | set(CFLAGS) 38 | list(APPEND CFLAGS 39 | -O2 40 | -Wall 41 | -Wno-pointer-sign 42 | -Wno-deprecated-declarations 43 | -Wstrict-prototypes 44 | -Wmissing-prototypes 45 | -Wmissing-declarations 46 | -Wshadow 47 | -Wpointer-arith 48 | -Wcast-qual 49 | -Wsign-compare 50 | "$<$:-O0;-g>" 51 | ) 52 | 53 | add_executable(cu 54 | command.c 55 | cu.c 56 | error.c 57 | input.c 58 | xmodem.c 59 | compat/closefrom.c 60 | compat/strtonum.c 61 | compat/vis.c 62 | ) 63 | 64 | target_compile_options(cu PRIVATE ${CFLAGS}) 65 | target_link_libraries(cu event) 66 | target_include_directories(cu PUBLIC 67 | compat 68 | ) 69 | 70 | install(TARGETS cu RUNTIME DESTINATION bin) 71 | set (CMAKE_INSTALL_MANDIR /usr/share/man) 72 | install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cu.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1/) 73 | -------------------------------------------------------------------------------- /input.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: input.c,v 1.2 2012/07/10 10:28:05 nicm Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2012 Nicholas Marriott 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "cu.h" 30 | 31 | /* 32 | * Prompt and read a line of user input from stdin. We want to use the termios 33 | * we were started with so restore and stick in a signal handler for ^C. 34 | */ 35 | 36 | volatile sig_atomic_t input_stop; 37 | 38 | void input_signal(int); 39 | 40 | void 41 | input_signal(int sig) 42 | { 43 | input_stop = 1; 44 | } 45 | 46 | const char * 47 | get_input(const char *prompt) 48 | { 49 | static char s[BUFSIZ]; 50 | struct sigaction act, oact; 51 | char c, *cp, *out = NULL; 52 | ssize_t n; 53 | 54 | memset(&act, 0, sizeof(act)); 55 | sigemptyset(&act.sa_mask); 56 | act.sa_flags = 0; 57 | act.sa_handler = input_signal; 58 | if (sigaction(SIGINT, &act, &oact) != 0) 59 | cu_err(1, "sigaction"); 60 | input_stop = 0; 61 | 62 | restore_termios(); 63 | 64 | printf("%s ", prompt); 65 | fflush(stdout); 66 | 67 | cp = s; 68 | while (cp != s + sizeof(s) - 1) { 69 | n = read(STDIN_FILENO, &c, 1); 70 | if (n == -1 && errno != EINTR) 71 | cu_err(1, "read"); 72 | if (n != 1 || input_stop) 73 | break; 74 | if (c == '\n') { 75 | out = s; 76 | break; 77 | } 78 | if (!iscntrl((u_char)c)) 79 | *cp++ = c; 80 | } 81 | *cp = '\0'; 82 | 83 | set_termios(); 84 | 85 | sigaction(SIGINT, &oact, NULL); 86 | 87 | return (out); 88 | } 89 | -------------------------------------------------------------------------------- /compat/vis.h: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: vis.h,v 1.11 2005/08/09 19:38:31 millert Exp $ */ 2 | /* $NetBSD: vis.h,v 1.4 1994/10/26 00:56:41 cgd Exp $ */ 3 | 4 | /*- 5 | * Copyright (c) 1990 The Regents of the University of California. 6 | * All rights reserved. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. Neither the name of the University nor the names of its contributors 17 | * may be used to endorse or promote products derived from this software 18 | * without specific prior written permission. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 | * SUCH DAMAGE. 31 | * 32 | * @(#)vis.h 5.9 (Berkeley) 4/3/91 33 | */ 34 | 35 | /* OPENBSD ORIGINAL: include/vis.h */ 36 | 37 | #ifdef HAVE_CONFIG_H 38 | #include "includes.h" 39 | #endif 40 | 41 | // #if !defined(HAVE_STRNVIS) 42 | 43 | #ifndef _VIS_H_ 44 | #define _VIS_H_ 45 | 46 | #include 47 | #include 48 | 49 | /* 50 | * to select alternate encoding format 51 | */ 52 | #define VIS_OCTAL 0x01 /* use octal \ddd format */ 53 | #define VIS_CSTYLE 0x02 /* use \[nrft0..] where appropriate */ 54 | 55 | /* 56 | * to alter set of characters encoded (default is to encode all 57 | * non-graphic except space, tab, and newline). 58 | */ 59 | #define VIS_SP 0x04 /* also encode space */ 60 | #define VIS_TAB 0x08 /* also encode tab */ 61 | #define VIS_NL 0x10 /* also encode newline */ 62 | #define VIS_WHITE (VIS_SP | VIS_TAB | VIS_NL) 63 | #define VIS_SAFE 0x20 /* only encode "unsafe" characters */ 64 | 65 | /* 66 | * other 67 | */ 68 | #define VIS_NOSLASH 0x40 /* inhibit printing '\' */ 69 | #define VIS_GLOB 0x100 /* encode glob(3) magics and '#' */ 70 | 71 | /* 72 | * unvis return codes 73 | */ 74 | #define UNVIS_VALID 1 /* character valid */ 75 | #define UNVIS_VALIDPUSH 2 /* character valid, push back passed char */ 76 | #define UNVIS_NOCHAR 3 /* valid sequence, no character produced */ 77 | #define UNVIS_SYNBAD -1 /* unrecognized escape sequence */ 78 | #define UNVIS_ERROR -2 /* decoder in unknown state (unrecoverable) */ 79 | 80 | /* 81 | * unvis flags 82 | */ 83 | #define UNVIS_END 1 /* no more characters */ 84 | 85 | char *vis(char *, int, int, int); 86 | 87 | #endif /* !_VIS_H_ */ 88 | 89 | //#endif /* !HAVE_STRNVIS */ 90 | -------------------------------------------------------------------------------- /compat/vis.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: vis.c,v 1.19 2005/09/01 17:15:49 millert Exp $ */ 2 | /*- 3 | * Copyright (c) 1989, 1993 4 | * The Regents of the University of California. All rights reserved. 5 | * 6 | * Redistribution and use in source and binary forms, with or without 7 | * modification, are permitted provided that the following conditions 8 | * are met: 9 | * 1. Redistributions of source code must retain the above copyright 10 | * notice, this list of conditions and the following disclaimer. 11 | * 2. Redistributions in binary form must reproduce the above copyright 12 | * notice, this list of conditions and the following disclaimer in the 13 | * documentation and/or other materials provided with the distribution. 14 | * 3. Neither the name of the University nor the names of its contributors 15 | * may be used to endorse or promote products derived from this software 16 | * without specific prior written permission. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | * SUCH DAMAGE. 29 | */ 30 | 31 | /* OPENBSD ORIGINAL: lib/libc/gen/vis.c */ 32 | 33 | #if !defined(HAVE_STRNVIS) 34 | 35 | #include 36 | #include 37 | 38 | #include "vis.h" 39 | 40 | #define isoctal(c) (((u_char)(c)) >= '0' && ((u_char)(c)) <= '7') 41 | #define isvisible(c) \ 42 | (((u_int)(c) <= UCHAR_MAX && isascii((u_char)(c)) && \ 43 | (((c) != '*' && (c) != '?' && (c) != '[' && (c) != '#') || \ 44 | (flag & VIS_GLOB) == 0) && isgraph((u_char)(c))) || \ 45 | ((flag & VIS_SP) == 0 && (c) == ' ') || \ 46 | ((flag & VIS_TAB) == 0 && (c) == '\t') || \ 47 | ((flag & VIS_NL) == 0 && (c) == '\n') || \ 48 | ((flag & VIS_SAFE) && ((c) == '\b' || \ 49 | (c) == '\007' || (c) == '\r' || \ 50 | isgraph((u_char)(c))))) 51 | 52 | /* 53 | * vis - visually encode characters 54 | */ 55 | char * 56 | vis(char *dst, int c, int flag, int nextc) 57 | { 58 | if (isvisible(c)) { 59 | *dst++ = c; 60 | if (c == '\\' && (flag & VIS_NOSLASH) == 0) 61 | *dst++ = '\\'; 62 | *dst = '\0'; 63 | return (dst); 64 | } 65 | 66 | if (flag & VIS_CSTYLE) { 67 | switch(c) { 68 | case '\n': 69 | *dst++ = '\\'; 70 | *dst++ = 'n'; 71 | goto done; 72 | case '\r': 73 | *dst++ = '\\'; 74 | *dst++ = 'r'; 75 | goto done; 76 | case '\b': 77 | *dst++ = '\\'; 78 | *dst++ = 'b'; 79 | goto done; 80 | case '\a': 81 | *dst++ = '\\'; 82 | *dst++ = 'a'; 83 | goto done; 84 | case '\v': 85 | *dst++ = '\\'; 86 | *dst++ = 'v'; 87 | goto done; 88 | case '\t': 89 | *dst++ = '\\'; 90 | *dst++ = 't'; 91 | goto done; 92 | case '\f': 93 | *dst++ = '\\'; 94 | *dst++ = 'f'; 95 | goto done; 96 | case ' ': 97 | *dst++ = '\\'; 98 | *dst++ = 's'; 99 | goto done; 100 | case '\0': 101 | *dst++ = '\\'; 102 | *dst++ = '0'; 103 | if (isoctal(nextc)) { 104 | *dst++ = '0'; 105 | *dst++ = '0'; 106 | } 107 | goto done; 108 | } 109 | } 110 | if (((c & 0177) == ' ') || (flag & VIS_OCTAL) || 111 | ((flag & VIS_GLOB) && (c == '*' || c == '?' || c == '[' || c == '#'))) { 112 | *dst++ = '\\'; 113 | *dst++ = ((u_char)c >> 6 & 07) + '0'; 114 | *dst++ = ((u_char)c >> 3 & 07) + '0'; 115 | *dst++ = ((u_char)c & 07) + '0'; 116 | goto done; 117 | } 118 | if ((flag & VIS_NOSLASH) == 0) 119 | *dst++ = '\\'; 120 | if (c & 0200) { 121 | c &= 0177; 122 | *dst++ = 'M'; 123 | } 124 | if (iscntrl((u_char)c)) { 125 | *dst++ = '^'; 126 | if (c == 0177) 127 | *dst++ = '?'; 128 | else 129 | *dst++ = c + '@'; 130 | } else { 131 | *dst++ = '-'; 132 | *dst++ = c; 133 | } 134 | done: 135 | *dst = '\0'; 136 | return (dst); 137 | } 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /xmodem.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: xmodem.c,v 1.9 2016/02/04 18:33:30 millert Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2012 Nicholas Marriott 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "cu.h" 30 | 31 | #define XMODEM_BLOCK 128 32 | #define XMODEM_RETRIES 10 33 | 34 | #define XMODEM_SOH '\001' 35 | #define XMODEM_EOT '\004' 36 | #define XMODEM_ACK '\006' 37 | #define XMODEM_NAK '\025' 38 | #define XMODEM_SUB '\032' 39 | #define XMODEM_C '\103' 40 | 41 | volatile sig_atomic_t xmodem_stop; 42 | 43 | void xmodem_signal(int sig); 44 | uint16_t xmodem_crc16(const u_char *buf, size_t len); 45 | int xmodem_read(char *c); 46 | int xmodem_write(const u_char *buf, size_t len); 47 | 48 | void 49 | xmodem_signal(int sig) 50 | { 51 | xmodem_stop = 1; 52 | } 53 | 54 | uint16_t 55 | xmodem_crc16(const u_char *buf, size_t len) 56 | { 57 | uint16_t crc; 58 | u_int i, j; 59 | 60 | crc = 0; 61 | for (i = 0; i < len; i++) { 62 | crc = crc ^ *buf++ << 8; 63 | for (j = 0; j < 8; j++) 64 | if (crc & 0x8000) 65 | crc = crc << 1 ^ 0x1021; 66 | else 67 | crc = crc << 1; 68 | } 69 | return (crc); 70 | } 71 | 72 | int 73 | xmodem_read(char *c) 74 | { 75 | for (;;) { 76 | switch (read(line_fd, c, 1)) { 77 | case -1: 78 | if (errno == EINTR && !xmodem_stop) 79 | continue; 80 | return (-1); 81 | case 0: 82 | errno = EPIPE; 83 | return (-1); 84 | case 1: 85 | return (0); 86 | } 87 | } 88 | } 89 | 90 | int 91 | xmodem_write(const u_char *buf, size_t len) 92 | { 93 | ssize_t n; 94 | 95 | while (len > 0) { 96 | n = write(line_fd, buf, len); 97 | if (n == -1) { 98 | if (errno == EINTR && !xmodem_stop) 99 | continue; 100 | return (-1); 101 | } 102 | buf += n; 103 | len -= n; 104 | } 105 | return (0); 106 | } 107 | 108 | void 109 | xmodem_send(const char *file) 110 | { 111 | FILE *f; 112 | u_char buf[3 + XMODEM_BLOCK + 2], c; 113 | size_t len, pktlen; 114 | uint8_t num; 115 | uint16_t crc; 116 | int crc_mode; 117 | u_int i, total; 118 | struct termios tio; 119 | struct sigaction act, oact; 120 | 121 | f = fopen(file, "r"); 122 | if (f == NULL) { 123 | cu_warn("%s", file); 124 | return; 125 | } 126 | 127 | memset(&act, 0, sizeof(act)); 128 | sigemptyset(&act.sa_mask); 129 | act.sa_flags = 0; 130 | act.sa_handler = xmodem_signal; 131 | if (sigaction(SIGINT, &act, &oact) != 0) 132 | cu_err(1, "sigaction"); 133 | xmodem_stop = 0; 134 | 135 | if (isatty(STDIN_FILENO)) { 136 | memcpy(&tio, &saved_tio, sizeof(tio)); 137 | tio.c_lflag &= ~ECHO; 138 | if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio) != 0) 139 | cu_err(1, "tcsetattr"); 140 | } 141 | set_blocking(line_fd, 1); 142 | tcflush(line_fd, TCIFLUSH); 143 | 144 | if (xmodem_read(&c) != 0) 145 | goto fail; 146 | if (c == XMODEM_C) 147 | crc_mode = 1; 148 | else if (c == XMODEM_NAK) 149 | crc_mode = 0; 150 | else { 151 | cu_warnx("%s: unexpected response \\%03hho", file, c); 152 | goto fail; 153 | } 154 | 155 | num = 1; 156 | total = 1; 157 | pktlen = 3 + XMODEM_BLOCK + (crc_mode ? 2 : 1); 158 | for (;;) { 159 | len = fread(buf + 3, 1, XMODEM_BLOCK, f); 160 | if (len == 0) 161 | break; 162 | memset(buf + 3 + len, XMODEM_SUB, XMODEM_BLOCK - len); 163 | 164 | buf[0] = XMODEM_SOH; 165 | buf[1] = num; 166 | buf[2] = 255 - num; 167 | 168 | if (crc_mode) { 169 | crc = xmodem_crc16(buf + 3, XMODEM_BLOCK); 170 | buf[3 + XMODEM_BLOCK] = crc >> 8; 171 | buf[3 + XMODEM_BLOCK + 1] = crc & 0xFF; 172 | } else { 173 | buf[3 + XMODEM_BLOCK] = 0; 174 | for (i = 0; i < XMODEM_BLOCK; i++) 175 | buf[3 + XMODEM_BLOCK] += buf[3 + i]; 176 | } 177 | 178 | for (i = 0; i < XMODEM_RETRIES; i++) { 179 | if (xmodem_stop) { 180 | errno = EINTR; 181 | goto fail; 182 | } 183 | cu_warnx("%s: sending block %u (attempt %u)", file, 184 | total, 1 + i); 185 | if (xmodem_write(buf, pktlen) != 0) 186 | goto fail; 187 | 188 | if (xmodem_read(&c) != 0) 189 | goto fail; 190 | if (c == XMODEM_ACK) 191 | break; 192 | if (c != XMODEM_NAK) { 193 | cu_warnx("%s: unexpected response \\%03hho", 194 | file, c); 195 | } 196 | } 197 | if (i == XMODEM_RETRIES) { 198 | cu_warnx("%s: too many retries", file); 199 | goto out; 200 | } 201 | 202 | if (len < XMODEM_BLOCK) 203 | break; 204 | num++; 205 | total++; 206 | } 207 | 208 | buf[0] = XMODEM_EOT; 209 | if (xmodem_write(buf, 1) != 0) 210 | goto fail; 211 | cu_warnx("%s: completed %u blocks", file, num); 212 | 213 | goto out; 214 | 215 | fail: 216 | cu_warn("%s", file); 217 | 218 | out: 219 | set_blocking(line_fd, 0); 220 | set_termios(); 221 | 222 | sigaction(SIGINT, &oact, NULL); 223 | 224 | fclose(f); 225 | } 226 | -------------------------------------------------------------------------------- /cu.1: -------------------------------------------------------------------------------- 1 | .\" $OpenBSD: cu.1,v 1.21 2020/12/12 06:35:37 bentley Exp $ 2 | .\" 3 | .\" Copyright (c) 1980, 1990, 1993 4 | .\" The Regents of the University of California. All rights reserved. 5 | .\" 6 | .\" Redistribution and use in source and binary forms, with or without 7 | .\" modification, are permitted provided that the following conditions 8 | .\" are met: 9 | .\" 1. Redistributions of source code must retain the above copyright 10 | .\" notice, this list of conditions and the following disclaimer. 11 | .\" 2. Redistributions in binary form must reproduce the above copyright 12 | .\" notice, this list of conditions and the following disclaimer in the 13 | .\" documentation and/or other materials provided with the distribution. 14 | .\" 3. Neither the name of the University nor the names of its contributors 15 | .\" may be used to endorse or promote products derived from this software 16 | .\" without specific prior written permission. 17 | .\" 18 | .\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 | .\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | .\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | .\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 | .\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | .\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 | .\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 | .\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 | .\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 | .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | .\" SUCH DAMAGE. 29 | .\" 30 | .Dd $Mdocdate: December 12 2020 $ 31 | .Dt CU 1 32 | .Os 33 | .Sh NAME 34 | .Nm cu 35 | .Nd serial terminal emulator 36 | .Sh SYNOPSIS 37 | .Nm 38 | .Op Fl dr 39 | .Op Fl E Ar escape_char 40 | .Op Fl l Ar line 41 | .Op Fl s Ar speed | Fl Ar speed 42 | .Nm 43 | .Op Ar host 44 | .Sh DESCRIPTION 45 | .Nm 46 | is used to connect to another system over a serial link. 47 | In the era before modern networks, it was typically used to 48 | connect to a modem in order to dial in to a remote host. 49 | It is now frequently used for tasks such as attaching to the 50 | serial console of another machine for administrative or 51 | debugging purposes. 52 | .Pp 53 | The options are as follows: 54 | .Bl -tag -width 4n 55 | .It Fl d 56 | Specify that the line is directly connected and 57 | .Nm 58 | should not allow the driver to block waiting for a carrier to be detected. 59 | .It Fl E Ar escape_char 60 | Specify an escape character to use instead of the default tilde. 61 | .It Fl l Ar line 62 | Specify the line to use. 63 | Either of the forms like 64 | .Pa cua00 65 | or 66 | .Pa /dev/cua00 67 | are permitted. 68 | The default is 69 | .Pa /dev/cua00 . 70 | See 71 | .Xr cua 4 72 | for information on terminal devices. 73 | Users in group 74 | .Dq dialer 75 | are permitted to use 76 | .Xr cua 4 77 | devices by default. 78 | .It Fl r 79 | Start 80 | .Nm 81 | in restricted mode. 82 | This prevents all local filesystem operations 83 | .Po 84 | .Cm ~R , 85 | .Cm ~X , 86 | and 87 | .Cm ~> 88 | .Pc 89 | and command executions 90 | .Po 91 | .Cm ~C 92 | and 93 | .Cm ~$ 94 | .Pc . 95 | .It Fl s Ar speed | Fl Ar speed 96 | Set the speed of the connection. 97 | The default is 9600. 98 | .El 99 | .Pp 100 | If 101 | .Ar host 102 | is given, 103 | .Nm 104 | uses the 105 | .Xr remote 5 106 | database to retrieve the 107 | .Sy dc Pq directly connected , 108 | .Sy dv Pq device 109 | and 110 | .Sy br Pq baud rate 111 | capabilities for that host. 112 | The 113 | .Nm 114 | utility ignores other capabilities found in that database. 115 | .Pp 116 | Typed characters are normally transmitted directly to the remote 117 | machine (which does the echoing as well). 118 | A tilde 119 | .Pq Ql ~ 120 | appearing as the first character of a line is an escape signal; the 121 | following are recognized: 122 | .Bl -tag -offset indent -width Fl 123 | .It Ic ~^D No or Ic ~. 124 | Drop the connection and exit. 125 | Only the connection is dropped \(en the login session is not terminated. 126 | .It Ic ~> 127 | Copy file from local to remote. 128 | .Nm 129 | prompts for the name of a local file to transmit. 130 | .It Ic ~$ 131 | Pipe the output from a local 132 | .Ux 133 | process to the remote host. 134 | The command string sent to the local 135 | .Ux 136 | system is processed by the shell. 137 | .It Ic ~# 138 | Send a 139 | .Dv BREAK 140 | to the remote system. 141 | .It Ic ~^Z 142 | Stop 143 | .Nm 144 | (only available with job control). 145 | .It Ic ~C 146 | Fork a child process on the local system to perform special protocols 147 | such as XMODEM. 148 | The child program will be run with the following arrangement of 149 | file descriptors: 150 | .Pp 151 | .Bl -item -compact -offset indent 152 | .It 153 | 0 \(<> remote tty in 154 | .It 155 | 1 \(<> remote tty out 156 | .It 157 | 2 \(<> local tty stderr 158 | .El 159 | .It Ic ~D 160 | Deassert the data terminal ready (DTR) line briefly. 161 | .It Ic ~R 162 | Record all output from the remote system to a file. 163 | If the given file already exists, it is appended to. 164 | If no file is specified, any existing recording is stopped. 165 | .It Ic ~S 166 | Change the speed of the connection. 167 | .It Ic ~X 168 | Send a file with the XMODEM protocol. 169 | .It Ic ~? 170 | Get a summary of the tilde escapes. 171 | .El 172 | .Pp 173 | When 174 | .Nm 175 | prompts for an argument, for example during setup of a file transfer, 176 | the line typed may be edited with the standard erase and kill characters. 177 | A null line in response to a prompt, or an interrupt, will abort the 178 | dialogue and return the user to the remote machine. 179 | .Pp 180 | .Nm 181 | guards against multiple users connecting to a remote system by opening 182 | modems and terminal lines with exclusive access. 183 | .Sh ENVIRONMENT 184 | .Bl -tag -width REMOTEXXX 185 | .It Ev HOST 186 | The default value for 187 | .Ar host 188 | if none is specified via the command line. 189 | .It Ev REMOTE 190 | A system description, or an absolute path to a 191 | .Xr remote 5 192 | system description database. 193 | .El 194 | .Sh FILES 195 | .Bl -tag -width /etc/remote 196 | .It Pa /etc/remote 197 | host description file 198 | .El 199 | .Sh EXIT STATUS 200 | .Ex -std cu 201 | .Sh SEE ALSO 202 | .Xr remote 5 203 | .Sh HISTORY 204 | The 205 | .Nm 206 | command appeared in 207 | .At v7 . 208 | This version was written for 209 | .Ox 5.4 210 | by Nicholas Marriott. 211 | -------------------------------------------------------------------------------- /command.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: command.c,v 1.18 2019/06/28 13:35:00 deraadt Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2012 Nicholas Marriott 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "cu.h" 36 | 37 | void pipe_command(void); 38 | void connect_command(void); 39 | void send_file(void); 40 | void send_xmodem(void); 41 | void set_speed(void); 42 | void start_record(void); 43 | 44 | void 45 | pipe_command(void) 46 | { 47 | const char *cmd; 48 | pid_t pid; 49 | int fd; 50 | 51 | cmd = get_input("Local command?"); 52 | if (cmd == NULL || *cmd == '\0') 53 | return; 54 | 55 | restore_termios(); 56 | set_blocking(line_fd, 1); 57 | 58 | switch (pid = fork()) { 59 | case -1: 60 | cu_err(1, "fork"); 61 | case 0: 62 | fd = open(_PATH_DEVNULL, O_RDWR); 63 | if (fd == -1 || dup2(fd, STDIN_FILENO) == -1) 64 | _exit(1); 65 | close(fd); 66 | 67 | if (signal(SIGINT, SIG_DFL) == SIG_ERR) 68 | _exit(1); 69 | if (signal(SIGQUIT, SIG_DFL) == SIG_ERR) 70 | _exit(1); 71 | 72 | /* attach stdout to line */ 73 | if (dup2(line_fd, STDOUT_FILENO) == -1) 74 | _exit(1); 75 | 76 | #ifdef __OpenBSD__ 77 | if (closefrom(STDERR_FILENO + 1) != 0) 78 | _exit(1); 79 | #else 80 | closefrom(STDERR_FILENO + 1); 81 | #endif 82 | 83 | execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL); 84 | _exit(1); 85 | default: 86 | while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) 87 | /* nothing */; 88 | break; 89 | } 90 | 91 | set_blocking(line_fd, 0); 92 | set_termios(); 93 | } 94 | 95 | void 96 | connect_command(void) 97 | { 98 | const char *cmd; 99 | pid_t pid; 100 | 101 | /* 102 | * Fork a program with: 103 | * 0 <-> remote tty in 104 | * 1 <-> remote tty out 105 | * 2 <-> local tty stderr 106 | */ 107 | 108 | cmd = get_input("Local command?"); 109 | if (cmd == NULL || *cmd == '\0') 110 | return; 111 | 112 | restore_termios(); 113 | set_blocking(line_fd, 1); 114 | 115 | switch (pid = fork()) { 116 | case -1: 117 | cu_err(1, "fork"); 118 | case 0: 119 | if (signal(SIGINT, SIG_DFL) == SIG_ERR) 120 | _exit(1); 121 | if (signal(SIGQUIT, SIG_DFL) == SIG_ERR) 122 | _exit(1); 123 | 124 | /* attach stdout and stdin to line */ 125 | if (dup2(line_fd, STDOUT_FILENO) == -1) 126 | _exit(1); 127 | if (dup2(line_fd, STDIN_FILENO) == -1) 128 | _exit(1); 129 | 130 | #ifdef __OpenBSD__ 131 | if (closefrom(STDERR_FILENO + 1) != 0) 132 | _exit(1); 133 | #else 134 | closefrom(STDERR_FILENO + 1); 135 | #endif 136 | 137 | execl(_PATH_BSHELL, "sh", "-c", cmd, (char *)NULL); 138 | _exit(1); 139 | default: 140 | while (waitpid(pid, NULL, 0) == -1 && errno == EINTR) 141 | /* nothing */; 142 | break; 143 | } 144 | 145 | set_blocking(line_fd, 0); 146 | set_termios(); 147 | } 148 | 149 | void 150 | send_file(void) 151 | { 152 | const char *file; 153 | FILE *f; 154 | char buf[BUFSIZ], *expanded; 155 | size_t len; 156 | 157 | file = get_input("Local file?"); 158 | if (file == NULL || *file == '\0') 159 | return; 160 | 161 | expanded = tilde_expand(file); 162 | f = fopen(expanded, "r"); 163 | if (f == NULL) { 164 | cu_warn("%s", file); 165 | return; 166 | } 167 | 168 | while (!feof(f) && !ferror(f)) { 169 | len = fread(buf, 1, sizeof(buf), f); 170 | if (len != 0) 171 | bufferevent_write(line_ev, buf, len); 172 | } 173 | 174 | fclose(f); 175 | free(expanded); 176 | } 177 | 178 | void 179 | send_xmodem(void) 180 | { 181 | const char *file; 182 | char *expanded; 183 | 184 | file = get_input("Local file?"); 185 | if (file == NULL || *file == '\0') 186 | return; 187 | 188 | expanded = tilde_expand(file); 189 | xmodem_send(expanded); 190 | free(expanded); 191 | } 192 | 193 | void 194 | set_speed(void) 195 | { 196 | const char *s, *errstr; 197 | int speed; 198 | 199 | s = get_input("New speed?"); 200 | if (s == NULL || *s == '\0') 201 | return; 202 | 203 | speed = strtonum(s, 0, UINT_MAX, &errstr); 204 | if (errstr != NULL) { 205 | cu_warnx("speed is %s: %s", errstr, s); 206 | return; 207 | } 208 | 209 | if (set_line(speed) != 0) 210 | cu_warn("tcsetattr"); 211 | } 212 | 213 | void 214 | start_record(void) 215 | { 216 | const char *file; 217 | 218 | if (record_file != NULL) { 219 | fclose(record_file); 220 | record_file = NULL; 221 | } 222 | 223 | file = get_input("Record file?"); 224 | if (file == NULL || *file == '\0') 225 | return; 226 | 227 | record_file = fopen(file, "a"); 228 | if (record_file == NULL) 229 | cu_warnx("%s", file); 230 | } 231 | 232 | void 233 | do_command(char c) 234 | { 235 | char esc[4 + 1]; 236 | 237 | if (restricted && strchr("CRX$>", c) != NULL) { 238 | cu_warnx("~%c command is not allowed in restricted mode", c); 239 | return; 240 | } 241 | 242 | switch (c) { 243 | case '.': 244 | case '\004': /* ^D */ 245 | event_loopexit(NULL); 246 | break; 247 | case '\032': /* ^Z */ 248 | restore_termios(); 249 | kill(getpid(), SIGTSTP); 250 | set_termios(); 251 | break; 252 | case 'C': 253 | connect_command(); 254 | break; 255 | case 'D': { 256 | #ifdef __OpenBSD__ 257 | ioctl(line_fd, TIOCCDTR, NULL); 258 | sleep(1); 259 | ioctl(line_fd, TIOCSDTR, NULL); 260 | #else 261 | const int dtrbits = TIOCM_DTR; 262 | ioctl(line_fd, TIOCMBIC, &dtrbits); 263 | sleep(1); 264 | ioctl(line_fd, TIOCMBIS, &dtrbits); 265 | #endif 266 | break; 267 | } 268 | case 'R': 269 | start_record(); 270 | break; 271 | case 'S': 272 | set_speed(); 273 | break; 274 | case 'X': 275 | send_xmodem(); 276 | break; 277 | case '$': 278 | pipe_command(); 279 | break; 280 | case '>': 281 | send_file(); 282 | break; 283 | case '#': 284 | ioctl(line_fd, TIOCSBRK, NULL); 285 | sleep(1); 286 | ioctl(line_fd, TIOCCBRK, NULL); 287 | break; 288 | default: 289 | if ((u_char)c == escape_char) 290 | bufferevent_write(line_ev, &c, 1); 291 | break; 292 | case '?': 293 | vis(esc, escape_char, VIS_WHITE | VIS_NOSLASH, 0); 294 | printf("\r\n" 295 | "%s# send break\r\n" 296 | "%s$ pipe local command to remote host\r\n" 297 | "%s> send file to remote host\r\n" 298 | "%sC connect program to remote host\r\n" 299 | "%sD de-assert DTR line briefly\r\n" 300 | "%sR start recording to file\r\n" 301 | "%sS set speed\r\n" 302 | "%sX send file with XMODEM\r\n" 303 | "%s? get this summary\r\n", 304 | esc, esc, esc, esc, esc, esc, esc, esc, esc 305 | ); 306 | break; 307 | } 308 | } 309 | -------------------------------------------------------------------------------- /cu.c: -------------------------------------------------------------------------------- 1 | /* $OpenBSD: cu.c,v 1.28 2019/06/28 13:35:00 deraadt Exp $ */ 2 | 3 | /* 4 | * Copyright (c) 2012 Nicholas Marriott 5 | * 6 | * Permission to use, copy, modify, and distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15 | * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | #include "cu.h" 37 | 38 | extern char *__progname; 39 | 40 | FILE *record_file; 41 | struct termios saved_tio; 42 | struct bufferevent *input_ev; 43 | struct bufferevent *output_ev; 44 | int escape_char = '~'; 45 | int is_direct = -1; 46 | int restricted = 0; 47 | const char *line_path = NULL; 48 | int line_speed = -1; 49 | int line_fd; 50 | struct termios line_tio; 51 | struct bufferevent *line_ev; 52 | struct event sigterm_ev; 53 | struct event sighup_ev; 54 | enum { 55 | STATE_NONE, 56 | STATE_NEWLINE, 57 | STATE_ESCAPE 58 | } last_state = STATE_NEWLINE; 59 | 60 | # define __dead __attribute__((__noreturn__)) 61 | 62 | __dead void usage(void); 63 | void signal_event(int, short, void *); 64 | void stream_read(struct bufferevent *, void *); 65 | void stream_error(struct bufferevent *, short, void *); 66 | void line_read(struct bufferevent *, void *); 67 | void line_error(struct bufferevent *, short, void *); 68 | void try_remote(const char *, const char *, const char *); 69 | 70 | __dead void 71 | usage(void) 72 | { 73 | fprintf(stderr, "usage: %s [-dr] [-E escape_char] [-l line] " 74 | "[-s speed | -speed]\n", __progname); 75 | fprintf(stderr, " %s [host]\n", __progname); 76 | exit(1); 77 | } 78 | 79 | int 80 | main(int argc, char **argv) 81 | { 82 | const char *errstr; 83 | #ifdef __OpenBSD__ 84 | char *tmp, *s, *host; 85 | #else 86 | char *tmp, *host; 87 | #endif 88 | int opt, i, flags; 89 | 90 | #ifdef __OpenBSD__ 91 | if (pledge("stdio rpath wpath cpath getpw proc exec tty", 92 | NULL) == -1) 93 | err(1, "pledge"); 94 | #endif 95 | 96 | if (isatty(STDIN_FILENO) && tcgetattr(STDIN_FILENO, &saved_tio) != 0) 97 | err(1, "tcgetattr"); 98 | 99 | /* 100 | * Convert obsolescent -### speed to modern -s### syntax which getopt() 101 | * can handle. 102 | */ 103 | for (i = 1; i < argc; i++) { 104 | if (strcmp("--", argv[i]) == 0) 105 | break; 106 | if (argv[i][0] != '-' || !isdigit((u_char)argv[i][1])) 107 | continue; 108 | 109 | if (asprintf(&argv[i], "-s%s", &argv[i][1]) == -1) 110 | errx(1, "speed asprintf"); 111 | } 112 | 113 | while ((opt = getopt(argc, argv, "drE:l:s:")) != -1) { 114 | switch (opt) { 115 | case 'd': 116 | is_direct = 1; 117 | break; 118 | #ifdef __OpenBSD__ 119 | case 'r': 120 | if (pledge("stdio rpath wpath tty", NULL) == -1) 121 | err(1, "pledge"); 122 | restricted = 1; 123 | break; 124 | #endif 125 | case 'E': 126 | if (optarg[0] == '^' && optarg[2] == '\0' && 127 | (u_char)optarg[1] >= 64 && (u_char)optarg[1] < 128) 128 | escape_char = (u_char)optarg[1] & 31; 129 | else if (strlen(optarg) == 1) 130 | escape_char = (u_char)optarg[0]; 131 | else 132 | errx(1, "invalid escape character: %s", optarg); 133 | break; 134 | case 'l': 135 | line_path = optarg; 136 | break; 137 | case 's': 138 | line_speed = strtonum(optarg, 0, INT_MAX, &errstr); 139 | if (errstr != NULL) 140 | errx(1, "speed is %s: %s", errstr, optarg); 141 | break; 142 | default: 143 | usage(); 144 | } 145 | } 146 | argc -= optind; 147 | argv += optind; 148 | if (argc != 0 && argc != 1) 149 | usage(); 150 | 151 | if (line_path != NULL || line_speed != -1 || is_direct != -1) { 152 | if (argc != 0) 153 | usage(); 154 | } else { 155 | if (argc == 1) 156 | host = argv[0]; 157 | else 158 | host = getenv("HOST"); 159 | if (host != NULL && *host != '\0') { 160 | if (*host == '/') 161 | line_path = host; 162 | #ifdef __OpenBSD__ 163 | else { 164 | s = getenv("REMOTE"); 165 | if (s != NULL && *s == '/') 166 | try_remote(host, s, NULL); 167 | else 168 | try_remote(host, NULL, s); 169 | } 170 | #endif 171 | } 172 | } 173 | 174 | if (line_path == NULL) 175 | line_path = "/dev/ttyUSB0"; 176 | if (line_speed == -1) 177 | line_speed = 115200; 178 | if (is_direct == -1) 179 | is_direct = 0; 180 | 181 | if (strchr(line_path, '/') == NULL) { 182 | if (asprintf(&tmp, "%s%s", _PATH_DEV, line_path) == -1) 183 | err(1, "asprintf"); 184 | line_path = tmp; 185 | } 186 | 187 | flags = O_RDWR; 188 | if (is_direct) 189 | flags |= O_NONBLOCK; 190 | line_fd = open(line_path, flags); 191 | if (line_fd == -1) 192 | err(1, "open(\"%s\")", line_path); 193 | #ifdef __OpenBSD__ 194 | if (restricted && pledge("stdio tty", NULL) == -1) 195 | err(1, "pledge"); 196 | #endif 197 | if (!isatty(line_fd)) 198 | err(1, "%s", line_path); 199 | if (ioctl(line_fd, TIOCEXCL) != 0) 200 | err(1, "ioctl(TIOCEXCL)"); 201 | if (tcgetattr(line_fd, &line_tio) != 0) 202 | err(1, "tcgetattr"); 203 | if (set_line(line_speed) != 0) 204 | err(1, "tcsetattr"); 205 | 206 | event_init(); 207 | 208 | signal_set(&sigterm_ev, SIGTERM, signal_event, NULL); 209 | signal_add(&sigterm_ev, NULL); 210 | signal_set(&sighup_ev, SIGHUP, signal_event, NULL); 211 | signal_add(&sighup_ev, NULL); 212 | if (signal(SIGINT, SIG_IGN) == SIG_ERR) 213 | err(1, "signal"); 214 | if (signal(SIGQUIT, SIG_IGN) == SIG_ERR) 215 | err(1, "signal"); 216 | 217 | set_termios(); /* after this use cu_err and friends */ 218 | 219 | /* stdin and stdout get separate events */ 220 | input_ev = bufferevent_new(STDIN_FILENO, stream_read, NULL, 221 | stream_error, NULL); 222 | bufferevent_enable(input_ev, EV_READ); 223 | output_ev = bufferevent_new(STDOUT_FILENO, NULL, NULL, stream_error, 224 | NULL); 225 | bufferevent_enable(output_ev, EV_WRITE); 226 | 227 | set_blocking(line_fd, 0); 228 | line_ev = bufferevent_new(line_fd, line_read, NULL, line_error, 229 | NULL); 230 | bufferevent_enable(line_ev, EV_READ|EV_WRITE); 231 | 232 | printf("Connected to %s (speed %d)\r\n", line_path, line_speed); 233 | event_dispatch(); 234 | 235 | restore_termios(); 236 | printf("\r\n[EOT]\n"); 237 | 238 | exit(0); 239 | } 240 | 241 | void 242 | signal_event(int fd, short events, void *data) 243 | { 244 | restore_termios(); 245 | #ifdef __OpenBSD__ 246 | printf("\r\n[SIG%s]\n", sys_signame[fd]); 247 | #else 248 | printf("\r\n[SIG%s]\n", strsignal(fd)); 249 | #endif 250 | 251 | exit(0); 252 | } 253 | 254 | void 255 | set_blocking(int fd, int state) 256 | { 257 | int mode; 258 | 259 | state = state ? 0 : O_NONBLOCK; 260 | if ((mode = fcntl(fd, F_GETFL)) == -1) 261 | cu_err(1, "fcntl"); 262 | if ((mode & O_NONBLOCK) != state) { 263 | mode = (mode & ~O_NONBLOCK) | state; 264 | if (fcntl(fd, F_SETFL, mode) == -1) 265 | cu_err(1, "fcntl"); 266 | } 267 | } 268 | 269 | void 270 | set_termios(void) 271 | { 272 | struct termios tio; 273 | 274 | if (!isatty(STDIN_FILENO)) 275 | return; 276 | 277 | memcpy(&tio, &saved_tio, sizeof(tio)); 278 | tio.c_lflag &= ~(ICANON|IEXTEN|ECHO); 279 | tio.c_iflag &= ~(INPCK|ICRNL); 280 | tio.c_oflag &= ~OPOST; 281 | tio.c_cc[VMIN] = 1; 282 | tio.c_cc[VTIME] = 0; 283 | tio.c_cc[VDISCARD] = _POSIX_VDISABLE; 284 | #ifdef __OpenBSD__ 285 | tio.c_cc[VDSUSP] = _POSIX_VDISABLE; 286 | #endif 287 | tio.c_cc[VINTR] = _POSIX_VDISABLE; 288 | tio.c_cc[VLNEXT] = _POSIX_VDISABLE; 289 | tio.c_cc[VQUIT] = _POSIX_VDISABLE; 290 | tio.c_cc[VSUSP] = _POSIX_VDISABLE; 291 | if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &tio) != 0) 292 | cu_err(1, "tcsetattr"); 293 | } 294 | 295 | void 296 | restore_termios(void) 297 | { 298 | if (isatty(STDIN_FILENO)) 299 | tcsetattr(STDIN_FILENO, TCSAFLUSH, &saved_tio); 300 | } 301 | 302 | int 303 | set_line(int speed) 304 | { 305 | struct termios tio; 306 | 307 | memcpy(&tio, &line_tio, sizeof(tio)); 308 | tio.c_iflag &= ~(ISTRIP|ICRNL); 309 | tio.c_oflag &= ~OPOST; 310 | tio.c_lflag &= ~(ICANON|ISIG|IEXTEN|ECHO); 311 | tio.c_cflag &= ~(CSIZE|PARENB); 312 | tio.c_cflag |= CREAD|CS8|CLOCAL; 313 | tio.c_cc[VMIN] = 1; 314 | tio.c_cc[VTIME] = 0; 315 | cfsetspeed(&tio, speed); 316 | if (tcsetattr(line_fd, TCSAFLUSH, &tio) != 0) 317 | return (-1); 318 | return (0); 319 | } 320 | 321 | void 322 | stream_read(struct bufferevent *bufev, void *data) 323 | { 324 | char *new_data, *ptr; 325 | size_t new_size; 326 | int state_change; 327 | 328 | new_data = EVBUFFER_DATA(input_ev->input); 329 | new_size = EVBUFFER_LENGTH(input_ev->input); 330 | if (new_size == 0) 331 | return; 332 | 333 | state_change = isatty(STDIN_FILENO); 334 | for (ptr = new_data; ptr < new_data + new_size; ptr++) { 335 | switch (last_state) { 336 | case STATE_NONE: 337 | if (state_change && *ptr == '\r') 338 | last_state = STATE_NEWLINE; 339 | break; 340 | case STATE_NEWLINE: 341 | if (state_change && (u_char)*ptr == escape_char) { 342 | last_state = STATE_ESCAPE; 343 | continue; 344 | } 345 | if (*ptr != '\r') 346 | last_state = STATE_NONE; 347 | break; 348 | case STATE_ESCAPE: 349 | do_command(*ptr); 350 | last_state = STATE_NEWLINE; 351 | continue; 352 | } 353 | 354 | bufferevent_write(line_ev, ptr, 1); 355 | } 356 | 357 | evbuffer_drain(input_ev->input, new_size); 358 | } 359 | 360 | void 361 | stream_error(struct bufferevent *bufev, short what, void *data) 362 | { 363 | event_loopexit(NULL); 364 | } 365 | 366 | void 367 | line_read(struct bufferevent *bufev, void *data) 368 | { 369 | char *new_data; 370 | size_t new_size; 371 | 372 | new_data = EVBUFFER_DATA(line_ev->input); 373 | new_size = EVBUFFER_LENGTH(line_ev->input); 374 | if (new_size == 0) 375 | return; 376 | 377 | if (record_file != NULL) 378 | fwrite(new_data, 1, new_size, record_file); 379 | bufferevent_write(output_ev, new_data, new_size); 380 | 381 | evbuffer_drain(line_ev->input, new_size); 382 | } 383 | 384 | void 385 | line_error(struct bufferevent *bufev, short what, void *data) 386 | { 387 | event_loopexit(NULL); 388 | } 389 | 390 | #ifdef __OpenBSD__ 391 | void 392 | try_remote(const char *host, const char *path, const char *entry) 393 | { 394 | const char *paths[] = { "/etc/remote", NULL, NULL }; 395 | char *cp, *s; 396 | long l; 397 | int error; 398 | 399 | if (path != NULL) { 400 | paths[0] = path; 401 | paths[1] = "/etc/remote"; 402 | } 403 | 404 | if (entry != NULL && cgetset(entry) != 0) 405 | cu_errx(1, "cgetset failed"); 406 | error = cgetent(&cp, (char **)paths, (char *)host); 407 | if (error < 0) { 408 | switch (error) { 409 | case -1: 410 | cu_errx(1, "unknown host %s", host); 411 | case -2: 412 | cu_errx(1, "can't open remote file"); 413 | case -3: 414 | cu_errx(1, "loop in remote file"); 415 | default: 416 | cu_errx(1, "unknown error in remote file"); 417 | } 418 | } 419 | 420 | if (is_direct == -1 && cgetcap(cp, "dc", ':') != NULL) 421 | is_direct = 1; 422 | 423 | if (line_path == NULL && cgetstr(cp, "dv", &s) >= 0) 424 | line_path = s; 425 | 426 | if (line_speed == -1 && cgetnum(cp, "br", &l) >= 0) { 427 | if (l < 0 || l > INT_MAX) 428 | cu_errx(1, "speed out of range"); 429 | line_speed = l; 430 | } 431 | } 432 | #endif 433 | 434 | /* Expands tildes in the file name. Based on code from ssh/misc.c. */ 435 | char * 436 | tilde_expand(const char *filename1) 437 | { 438 | const char *filename, *path, *sep; 439 | char user[128], *out; 440 | struct passwd *pw; 441 | u_int len, slash; 442 | int rv; 443 | 444 | if (*filename1 != '~') 445 | goto no_change; 446 | filename = filename1 + 1; 447 | 448 | path = strchr(filename, '/'); 449 | if (path != NULL && path > filename) { /* ~user/path */ 450 | slash = path - filename; 451 | if (slash > sizeof(user) - 1) 452 | goto no_change; 453 | memcpy(user, filename, slash); 454 | user[slash] = '\0'; 455 | if ((pw = getpwnam(user)) == NULL) 456 | goto no_change; 457 | } else if ((pw = getpwuid(getuid())) == NULL) /* ~/path */ 458 | goto no_change; 459 | 460 | /* Make sure directory has a trailing '/' */ 461 | len = strlen(pw->pw_dir); 462 | if (len == 0 || pw->pw_dir[len - 1] != '/') 463 | sep = "/"; 464 | else 465 | sep = ""; 466 | 467 | /* Skip leading '/' from specified path */ 468 | if (path != NULL) 469 | filename = path + 1; 470 | 471 | if ((rv = asprintf(&out, "%s%s%s", pw->pw_dir, sep, filename)) == -1) 472 | cu_err(1, "asprintf"); 473 | if (rv >= PATH_MAX) { 474 | free(out); 475 | goto no_change; 476 | } 477 | 478 | return (out); 479 | 480 | no_change: 481 | out = strdup(filename1); 482 | if (out == NULL) 483 | cu_err(1, "strdup"); 484 | return (out); 485 | } 486 | --------------------------------------------------------------------------------