1 /* $OpenBSD: cu.c,v 1.14 2013/04/24 16:01:10 tedu Exp $ */
4 * Copyright (c) 2012 Nicholas Marriott <nicm@openbsd.org>
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.
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.
19 #include <sys/param.h>
20 #include <sys/ioctl.h>
38 extern char *__progname
;
41 struct termios saved_tio
;
42 struct bufferevent
*input_ev
;
43 struct bufferevent
*output_ev
;
45 struct termios line_tio
;
46 struct bufferevent
*line_ev
;
47 struct event sigterm_ev
;
48 struct event sighup_ev
;
53 } last_state
= STATE_NEWLINE
;
56 #define __dead __attribute__((__noreturn__))
59 __dead
void usage(void);
60 void signal_event(int, short, void *);
61 void stream_read(struct bufferevent
*, void *);
62 void stream_error(struct bufferevent
*, short, void *);
63 void line_read(struct bufferevent
*, void *);
64 void line_error(struct bufferevent
*, short, void *);
69 fprintf(stderr
, "usage: %s [-l line] [-s speed | -speed]\n",
75 main(int argc
, char **argv
)
77 const char *line
, *errstr
;
85 * Convert obsolescent -### speed to modern -s### syntax which getopt()
88 for (i
= 1; i
< argc
; i
++) {
89 if (strcmp("--", argv
[i
]) == 0)
91 if (argv
[i
][0] != '-' || !isdigit(argv
[i
][1]))
94 if (asprintf(&argv
[i
], "-s%s", &argv
[i
][1]) == -1)
95 errx(1, "speed asprintf");
98 while ((opt
= getopt(argc
, argv
, "l:s:")) != -1) {
104 speed
= strtonum(optarg
, 0, UINT_MAX
, &errstr
);
106 errx(1, "speed is %s: %s", errstr
, optarg
);
117 if (strchr(line
, '/') == NULL
) {
118 if (asprintf(&tmp
, "%s%s", _PATH_DEV
, line
) == -1)
123 line_fd
= open(line
, O_RDWR
);
125 err(1, "open(\"%s\")", line
);
126 if (ioctl(line_fd
, TIOCEXCL
) != 0)
127 err(1, "ioctl(TIOCEXCL)");
128 if (tcgetattr(line_fd
, &line_tio
) != 0)
130 if (set_line(speed
) != 0)
133 if (isatty(STDIN_FILENO
) && tcgetattr(STDIN_FILENO
, &saved_tio
) != 0)
138 signal_set(&sigterm_ev
, SIGTERM
, signal_event
, NULL
);
139 signal_add(&sigterm_ev
, NULL
);
140 signal_set(&sighup_ev
, SIGHUP
, signal_event
, NULL
);
141 signal_add(&sighup_ev
, NULL
);
142 if (signal(SIGINT
, SIG_IGN
) == SIG_ERR
)
144 if (signal(SIGQUIT
, SIG_IGN
) == SIG_ERR
)
147 set_termios(); /* after this use cu_err and friends */
149 /* stdin and stdout get separate events */
150 input_ev
= bufferevent_new(STDIN_FILENO
, stream_read
, NULL
,
152 bufferevent_enable(input_ev
, EV_READ
);
153 output_ev
= bufferevent_new(STDOUT_FILENO
, NULL
, NULL
, stream_error
,
155 bufferevent_enable(output_ev
, EV_WRITE
);
157 line_ev
= bufferevent_new(line_fd
, line_read
, NULL
, line_error
,
159 bufferevent_enable(line_ev
, EV_READ
|EV_WRITE
);
161 printf("Connected (speed %d)\r\n", speed
);
165 printf("\r\n[EOT]\n");
173 short events
__attribute__((unused
)),
174 void *data
__attribute__((unused
)))
177 printf("\r\n[SIG%d]\n", fd
);
187 if (!isatty(STDIN_FILENO
))
190 memcpy(&tio
, &saved_tio
, sizeof(tio
));
191 tio
.c_lflag
&= ~(ICANON
|IEXTEN
|ECHO
);
192 tio
.c_iflag
&= ~(INPCK
|ICRNL
);
193 tio
.c_iflag
&= ~(IXON
); /* disable XON/XOFF flow control on output. */
194 tio
.c_oflag
&= ~OPOST
;
197 tio
.c_cc
[VDISCARD
] = _POSIX_VDISABLE
;
199 tio
.c_cc
[VDSUSP
] = _POSIX_VDISABLE
;
201 tio
.c_cc
[VINTR
] = _POSIX_VDISABLE
;
202 tio
.c_cc
[VLNEXT
] = _POSIX_VDISABLE
;
203 tio
.c_cc
[VQUIT
] = _POSIX_VDISABLE
;
204 tio
.c_cc
[VSUSP
] = _POSIX_VDISABLE
;
205 if (tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &tio
) != 0)
206 cu_err(1, "tcsetattr");
210 restore_termios(void)
212 if (isatty(STDIN_FILENO
))
213 tcsetattr(STDIN_FILENO
, TCSAFLUSH
, &saved_tio
);
221 memcpy(&tio
, &line_tio
, sizeof(tio
));
222 tio
.c_iflag
&= ~(ISTRIP
|ICRNL
);
223 tio
.c_oflag
&= ~OPOST
;
224 tio
.c_lflag
&= ~(ICANON
|ISIG
|IEXTEN
|ECHO
);
225 tio
.c_cflag
&= ~(CSIZE
|PARENB
);
226 tio
.c_cflag
|= CREAD
|CS8
|CLOCAL
;
229 cfsetspeed(&tio
, speed
);
230 if (tcsetattr(line_fd
, TCSAFLUSH
, &tio
) != 0)
237 struct bufferevent
*bufev
__attribute__((unused
)),
238 void *data
__attribute__((unused
)))
240 unsigned char *new_data
, *ptr
;
244 new_data
= EVBUFFER_DATA(input_ev
->input
);
245 new_size
= EVBUFFER_LENGTH(input_ev
->input
);
249 state_change
= isatty(STDIN_FILENO
);
250 for (ptr
= new_data
; ptr
< new_data
+ new_size
; ptr
++) {
251 switch (last_state
) {
253 if (state_change
&& *ptr
== '\r')
254 last_state
= STATE_NEWLINE
;
257 if (state_change
&& *ptr
== '~') {
258 last_state
= STATE_TILDE
;
262 last_state
= STATE_NONE
;
266 last_state
= STATE_NEWLINE
;
270 bufferevent_write(line_ev
, ptr
, 1);
273 evbuffer_drain(input_ev
->input
, new_size
);
278 struct bufferevent
*bufev
__attribute__((unused
)),
279 short what
__attribute__((unused
)),
280 void *data
__attribute__((unused
)))
282 event_loopexit(NULL
);
287 struct bufferevent
*bufev
__attribute__((unused
)),
288 void *data
__attribute__((unused
)))
290 unsigned char *new_data
;
293 new_data
= EVBUFFER_DATA(line_ev
->input
);
294 new_size
= EVBUFFER_LENGTH(line_ev
->input
);
298 if (record_file
!= NULL
)
299 fwrite(new_data
, 1, new_size
, record_file
);
300 bufferevent_write(output_ev
, new_data
, new_size
);
302 evbuffer_drain(line_ev
->input
, new_size
);
307 struct bufferevent
*bufev
__attribute__((unused
)),
308 short what
__attribute__((unused
)),
309 void *data
__attribute__((unused
)))
311 event_loopexit(NULL
);
314 /* Expands tildes in the file name. Based on code from ssh/misc.c. */
316 tilde_expand(const char *filename1
)
318 const char *filename
, *path
, *sep
;
319 char user
[128], *out
;
324 if (*filename1
!= '~')
326 filename
= filename1
+ 1;
328 path
= strchr(filename
, '/');
329 if (path
!= NULL
&& path
> filename
) { /* ~user/path */
330 slash
= path
- filename
;
331 if (slash
> sizeof(user
) - 1)
333 memcpy(user
, filename
, slash
);
335 if ((pw
= getpwnam(user
)) == NULL
)
337 } else if ((pw
= getpwuid(getuid())) == NULL
) /* ~/path */
340 /* Make sure directory has a trailing '/' */
341 len
= strlen(pw
->pw_dir
);
342 if (len
== 0 || pw
->pw_dir
[len
- 1] != '/')
347 /* Skip leading '/' from specified path */
351 if ((rv
= asprintf(&out
, "%s%s%s", pw
->pw_dir
, sep
, filename
)) == -1)
352 cu_err(1, "asprintf");
353 if (rv
>= MAXPATHLEN
) {
361 out
= strdup(filename1
);