6 |
* |
* |
7 |
* option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95 |
* option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95 |
8 |
* |
* |
9 |
* 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org> |
* 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org> |
10 |
* - added Native Language Support |
* - added Native Language Support |
11 |
|
* |
12 |
* 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net> |
* 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net> |
13 |
* - enable hardware flow control before displaying /etc/issue |
* - enable hardware flow control before displaying /etc/issue |
14 |
* |
* |
15 |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
* Licensed under GPLv2 or later, see file LICENSE in this tarball for details. |
|
* |
|
16 |
*/ |
*/ |
17 |
|
|
18 |
#include "busybox.h" |
#include "libbb.h" |
19 |
#include <syslog.h> |
#include <syslog.h> |
20 |
|
|
21 |
#if ENABLE_FEATURE_UTMP |
#if ENABLE_FEATURE_UTMP |
22 |
#include <utmp.h> |
#include <utmp.h> /* updwtmp() */ |
23 |
#endif |
#endif |
24 |
|
|
25 |
/* |
/* |
27 |
* System V, assume it is SunOS 4. |
* System V, assume it is SunOS 4. |
28 |
*/ |
*/ |
29 |
#ifdef LOGIN_PROCESS /* defined in System V utmp.h */ |
#ifdef LOGIN_PROCESS /* defined in System V utmp.h */ |
|
#define SYSV_STYLE /* select System V style getty */ |
|
30 |
#include <sys/utsname.h> |
#include <sys/utsname.h> |
31 |
#include <time.h> |
#include <time.h> |
32 |
#if ENABLE_FEATURE_WTMP |
#else /* if !sysV style, wtmp/utmp code is off */ |
33 |
extern void updwtmp(const char *filename, const struct utmp *ut); |
#undef ENABLE_FEATURE_UTMP |
34 |
static void update_utmp(char *line); |
#undef ENABLE_FEATURE_WTMP |
35 |
#endif |
#define ENABLE_FEATURE_UTMP 0 |
36 |
|
#define ENABLE_FEATURE_WTMP 0 |
37 |
#endif /* LOGIN_PROCESS */ |
#endif /* LOGIN_PROCESS */ |
38 |
|
|
39 |
/* |
/* |
46 |
|
|
47 |
/* I doubt there are systems which still need this */ |
/* I doubt there are systems which still need this */ |
48 |
#undef HANDLE_ALLCAPS |
#undef HANDLE_ALLCAPS |
49 |
|
#undef ANCIENT_BS_KILL_CHARS |
50 |
|
|
51 |
#define _PATH_LOGIN "/bin/login" |
#define _PATH_LOGIN "/bin/login" |
52 |
|
|
57 |
#define ISSUE "/etc/issue" /* displayed before the login prompt */ |
#define ISSUE "/etc/issue" /* displayed before the login prompt */ |
58 |
|
|
59 |
/* Some shorthands for control characters. */ |
/* Some shorthands for control characters. */ |
60 |
#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */ |
#define CTL(x) ((x) ^ 0100) /* Assumes ASCII dialect */ |
61 |
#define CR CTL('M') /* carriage return */ |
#define CR CTL('M') /* carriage return */ |
62 |
#define NL CTL('J') /* line feed */ |
#define NL CTL('J') /* line feed */ |
63 |
#define BS CTL('H') /* back space */ |
#define BS CTL('H') /* back space */ |
76 |
* When multiple baud rates are specified on the command line, the first one |
* When multiple baud rates are specified on the command line, the first one |
77 |
* we will try is the first one specified. |
* we will try is the first one specified. |
78 |
*/ |
*/ |
|
#define FIRST_SPEED 0 |
|
|
|
|
|
/* Storage for command-line options. */ |
|
|
|
|
79 |
#define MAX_SPEED 10 /* max. nr. of baud rates */ |
#define MAX_SPEED 10 /* max. nr. of baud rates */ |
80 |
|
|
81 |
|
/* Storage for command-line options. */ |
82 |
struct options { |
struct options { |
83 |
int flags; /* toggle switches, see below */ |
int flags; /* toggle switches, see below */ |
84 |
unsigned timeout; /* time-out period */ |
unsigned timeout; /* time-out period */ |
85 |
char *login; /* login program */ |
const char *login; /* login program */ |
86 |
char *tty; /* name of tty */ |
const char *tty; /* name of tty */ |
87 |
char *initstring; /* modem init string */ |
const char *initstring; /* modem init string */ |
88 |
char *issue; /* alternative issue file */ |
const char *issue; /* alternative issue file */ |
89 |
int numspeed; /* number of baud rates to try */ |
int numspeed; /* number of baud rates to try */ |
90 |
int speeds[MAX_SPEED]; /* baud rates to be tried */ |
int speeds[MAX_SPEED]; /* baud rates to be tried */ |
91 |
}; |
}; |
92 |
|
|
|
static const char opt_string[] = "I:LH:f:hil:mt:wn"; |
|
|
#define F_INITSTRING (1<<0) /* initstring is set */ |
|
|
#define F_LOCAL (1<<1) /* force local */ |
|
|
#define F_FAKEHOST (1<<2) /* force fakehost */ |
|
|
#define F_CUSTISSUE (1<<3) /* give alternative issue file */ |
|
|
#define F_RTSCTS (1<<4) /* enable RTS/CTS flow control */ |
|
|
#define F_ISSUE (1<<5) /* display /etc/issue */ |
|
|
#define F_LOGIN (1<<6) /* non-default login program */ |
|
|
#define F_PARSE (1<<7) /* process modem status messages */ |
|
|
#define F_TIMEOUT (1<<8) /* time out */ |
|
|
#define F_WAITCRLF (1<<9) /* wait for CR or LF */ |
|
|
#define F_NOPROMPT (1<<10) /* don't ask for login name! */ |
|
|
|
|
93 |
/* Storage for things detected while the login name was read. */ |
/* Storage for things detected while the login name was read. */ |
94 |
struct chardata { |
struct chardata { |
95 |
unsigned char erase; /* erase character */ |
unsigned char erase; /* erase character */ |
96 |
unsigned char kill; /* kill character */ |
unsigned char kill; /* kill character */ |
97 |
unsigned char eol; /* end-of-line character */ |
unsigned char eol; /* end-of-line character */ |
98 |
unsigned char parity; /* what parity did we see */ |
unsigned char parity; /* what parity did we see */ |
99 |
|
/* (parity & 1): saw odd parity char with 7th bit set */ |
100 |
|
/* (parity & 2): saw even parity char with 7th bit set */ |
101 |
|
/* parity == 0: probably 7-bit, space parity? */ |
102 |
|
/* parity == 1: probably 7-bit, odd parity? */ |
103 |
|
/* parity == 2: probably 7-bit, even parity? */ |
104 |
|
/* parity == 3: definitely 8 bit, no parity! */ |
105 |
|
/* Hmm... with any value of "parity" 8 bit, no parity is possible */ |
106 |
#ifdef HANDLE_ALLCAPS |
#ifdef HANDLE_ALLCAPS |
107 |
unsigned char capslock; /* upper case without lower case */ |
unsigned char capslock; /* upper case without lower case */ |
108 |
#endif |
#endif |
109 |
}; |
}; |
110 |
|
|
111 |
|
|
112 |
/* Initial values for the above. */ |
/* Initial values for the above. */ |
113 |
static const struct chardata init_chardata = { |
static const struct chardata init_chardata = { |
114 |
DEF_ERASE, /* default erase character */ |
DEF_ERASE, /* default erase character */ |
120 |
#endif |
#endif |
121 |
}; |
}; |
122 |
|
|
123 |
/* The following is used for understandable diagnostics. */ |
static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn"; |
124 |
|
#define F_INITSTRING (1 << 0) /* -I initstring is set */ |
125 |
|
#define F_LOCAL (1 << 1) /* -L force local */ |
126 |
|
#define F_FAKEHOST (1 << 2) /* -H fake hostname */ |
127 |
|
#define F_CUSTISSUE (1 << 3) /* -f give alternative issue file */ |
128 |
|
#define F_RTSCTS (1 << 4) /* -h enable RTS/CTS flow control */ |
129 |
|
#define F_ISSUE (1 << 5) /* -i display /etc/issue */ |
130 |
|
#define F_LOGIN (1 << 6) /* -l non-default login program */ |
131 |
|
#define F_PARSE (1 << 7) /* -m process modem status messages */ |
132 |
|
#define F_TIMEOUT (1 << 8) /* -t time out */ |
133 |
|
#define F_WAITCRLF (1 << 9) /* -w wait for CR or LF */ |
134 |
|
#define F_NOPROMPT (1 << 10) /* -n don't ask for login name */ |
135 |
|
|
|
/* Fake hostname for ut_host specified on command line. */ |
|
|
static char *fakehost = NULL; |
|
136 |
|
|
137 |
/* ... */ |
#define line_buf bb_common_bufsiz1 |
138 |
|
|
139 |
|
/* The following is used for understandable diagnostics. */ |
140 |
#ifdef DEBUGGING |
#ifdef DEBUGGING |
|
#define debug(s) fprintf(dbf,s); fflush(dbf) |
|
|
#define DEBUGTERM "/dev/ttyp0" |
|
141 |
static FILE *dbf; |
static FILE *dbf; |
142 |
|
#define DEBUGTERM "/dev/ttyp0" |
143 |
|
#define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0) |
144 |
#else |
#else |
145 |
#define debug(s) /* nothing */ |
#define debug(...) ((void)0) |
146 |
#endif |
#endif |
147 |
|
|
148 |
|
|
149 |
/* bcode - convert speed string to speed code; return 0 on failure */ |
/* bcode - convert speed string to speed code; return <= 0 on failure */ |
150 |
static int bcode(const char *s) |
static int bcode(const char *s) |
151 |
{ |
{ |
152 |
int r; |
int value = bb_strtou(s, NULL, 10); /* yes, int is intended! */ |
153 |
unsigned value = bb_strtou(s, NULL, 10); |
if (value < 0) /* bad terminating char, overflow, etc */ |
154 |
if (errno) { |
return value; |
155 |
return -1; |
return tty_value_to_baud(value); |
|
} |
|
|
r = tty_value_to_baud(value); |
|
|
if (r > 0) { |
|
|
return r; |
|
|
} |
|
|
return 0; |
|
156 |
} |
} |
157 |
|
|
|
|
|
158 |
/* parse_speeds - parse alternate baud rates */ |
/* parse_speeds - parse alternate baud rates */ |
159 |
static void parse_speeds(struct options *op, char *arg) |
static void parse_speeds(struct options *op, char *arg) |
160 |
{ |
{ |
161 |
char *cp; |
char *cp; |
162 |
|
|
163 |
|
/* NB: at least one iteration is always done */ |
164 |
debug("entered parse_speeds\n"); |
debug("entered parse_speeds\n"); |
165 |
for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) { |
while ((cp = strsep(&arg, ",")) != NULL) { |
166 |
if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0) |
op->speeds[op->numspeed] = bcode(cp); |
167 |
|
if (op->speeds[op->numspeed] <= 0) |
168 |
bb_error_msg_and_die("bad speed: %s", cp); |
bb_error_msg_and_die("bad speed: %s", cp); |
169 |
|
op->numspeed++; |
170 |
if (op->numspeed > MAX_SPEED) |
if (op->numspeed > MAX_SPEED) |
171 |
bb_error_msg_and_die("too many alternate speeds"); |
bb_error_msg_and_die("too many alternate speeds"); |
172 |
} |
} |
173 |
debug("exiting parsespeeds\n"); |
debug("exiting parse_speeds\n"); |
174 |
} |
} |
175 |
|
|
|
|
|
176 |
/* parse_args - parse command-line arguments */ |
/* parse_args - parse command-line arguments */ |
177 |
static void parse_args(int argc, char **argv, struct options *op) |
static void parse_args(char **argv, struct options *op, char **fakehost_p) |
178 |
{ |
{ |
179 |
char *ts; |
char *ts; |
180 |
|
|
181 |
op->flags = getopt32(argc, argv, opt_string, |
opt_complementary = "-2:t+"; /* at least 2 args; -t N */ |
182 |
&(op->initstring), &fakehost, &(op->issue), |
op->flags = getopt32(argv, opt_string, |
183 |
&(op->login), &ts); |
&(op->initstring), fakehost_p, &(op->issue), |
184 |
|
&(op->login), &op->timeout); |
185 |
|
argv += optind; |
186 |
if (op->flags & F_INITSTRING) { |
if (op->flags & F_INITSTRING) { |
187 |
const char *p = op->initstring; |
const char *p = op->initstring; |
188 |
char *q; |
char *q; |
189 |
|
|
190 |
q = op->initstring = xstrdup(op->initstring); |
op->initstring = q = xstrdup(p); |
191 |
/* copy optarg into op->initstring decoding \ddd |
/* copy optarg into op->initstring decoding \ddd |
192 |
octal codes into chars */ |
octal codes into chars */ |
193 |
while (*p) { |
while (*p) { |
200 |
} |
} |
201 |
*q = '\0'; |
*q = '\0'; |
202 |
} |
} |
203 |
op->flags ^= F_ISSUE; /* revert flag show /etc/issue */ |
op->flags ^= F_ISSUE; /* invert flag "show /etc/issue" */ |
204 |
if (op->flags & F_TIMEOUT) { |
debug("after getopt\n"); |
|
op->timeout = xatoul_range(ts, 1, INT_MAX); |
|
|
} |
|
|
argv += optind; |
|
|
argc -= optind; |
|
|
debug("after getopt loop\n"); |
|
|
if (argc < 2) /* check parameter count */ |
|
|
bb_show_usage(); |
|
205 |
|
|
206 |
/* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ |
/* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ |
207 |
|
op->tty = argv[0]; /* tty name */ |
208 |
|
ts = argv[1]; /* baud rate(s) */ |
209 |
if (isdigit(argv[0][0])) { |
if (isdigit(argv[0][0])) { |
210 |
/* a number first, assume it's a speed (BSD style) */ |
/* a number first, assume it's a speed (BSD style) */ |
211 |
parse_speeds(op, argv[0]); /* baud rate(s) */ |
op->tty = ts; /* tty name is in argv[1] */ |
212 |
op->tty = argv[1]; /* tty name */ |
ts = argv[0]; /* baud rate(s) */ |
|
} else { |
|
|
op->tty = argv[0]; /* tty name */ |
|
|
parse_speeds(op, argv[1]); /* baud rate(s) */ |
|
213 |
} |
} |
214 |
|
parse_speeds(op, ts); |
215 |
|
|
216 |
if (argv[2]) |
// TODO: if applet_name is set to "getty: TTY", bb_error_msg's get simpler! |
217 |
setenv("TERM", argv[2], 1); |
// grep for "%s:" |
218 |
|
|
219 |
debug("exiting parseargs\n"); |
if (argv[2]) |
220 |
} |
xsetenv("TERM", argv[2]); |
221 |
|
|
222 |
static void xdup2(int srcfd, int dstfd, const char *tty) |
debug("exiting parse_args\n"); |
|
{ |
|
|
if (dup2(srcfd, dstfd) == -1) |
|
|
bb_perror_msg_and_die("%s: dup", tty); |
|
223 |
} |
} |
224 |
|
|
225 |
/* open_tty - set up tty as standard { input, output, error } */ |
/* open_tty - set up tty as standard { input, output, error } */ |
226 |
static void open_tty(char *tty, struct termios *tp, int local) |
static void open_tty(const char *tty) |
227 |
{ |
{ |
|
int chdir_to_root = 0; |
|
|
|
|
228 |
/* Set up new standard input, unless we are given an already opened port. */ |
/* Set up new standard input, unless we are given an already opened port. */ |
|
|
|
229 |
if (NOT_LONE_DASH(tty)) { |
if (NOT_LONE_DASH(tty)) { |
230 |
struct stat st; |
// struct stat st; |
231 |
int fd; |
// int cur_dir_fd; |
232 |
|
// int fd; |
233 |
|
|
234 |
/* Sanity checks... */ |
/* Sanity checks... */ |
235 |
|
// cur_dir_fd = xopen(".", O_DIRECTORY | O_NONBLOCK); |
236 |
|
// xchdir("/dev"); |
237 |
|
// xstat(tty, &st); |
238 |
|
// if ((st.st_mode & S_IFMT) != S_IFCHR) |
239 |
|
// bb_error_msg_and_die("%s: not a character device", tty); |
240 |
|
|
241 |
xchdir("/dev"); |
if (tty[0] != '/') |
242 |
chdir_to_root = 1; |
tty = xasprintf("/dev/%s", tty); /* will leak it */ |
|
xstat(tty, &st); |
|
|
if ((st.st_mode & S_IFMT) != S_IFCHR) |
|
|
bb_error_msg_and_die("%s: not a character device", tty); |
|
243 |
|
|
244 |
/* Open the tty as standard input. */ |
/* Open the tty as standard input. */ |
|
|
|
245 |
debug("open(2)\n"); |
debug("open(2)\n"); |
246 |
fd = xopen(tty, O_RDWR | O_NONBLOCK); |
close(0); |
247 |
xdup2(fd, 0, tty); |
/*fd =*/ xopen(tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */ |
248 |
while (fd > 2) close(fd--); |
|
249 |
|
// /* Restore current directory */ |
250 |
|
// fchdir(cur_dir_fd); |
251 |
|
|
252 |
|
/* Open the tty as standard input, continued */ |
253 |
|
// xmove_fd(fd, 0); |
254 |
|
// /* fd is >= cur_dir_fd, and cur_dir_fd gets closed too here: */ |
255 |
|
// while (fd > 2) |
256 |
|
// close(fd--); |
257 |
|
|
258 |
|
/* Set proper protections and ownership. */ |
259 |
|
fchown(0, 0, 0); /* 0:0 */ |
260 |
|
fchmod(0, 0620); /* crw--w---- */ |
261 |
} else { |
} else { |
262 |
/* |
/* |
263 |
* Standard input should already be connected to an open port. Make |
* Standard input should already be connected to an open port. Make |
264 |
* sure it is open for read/write. |
* sure it is open for read/write. |
265 |
*/ |
*/ |
266 |
|
if ((fcntl(0, F_GETFL) & O_RDWR) != O_RDWR) |
267 |
if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR) |
bb_error_msg_and_die("stdin is not open for read/write"); |
|
bb_error_msg_and_die("%s: not open for read/write", tty); |
|
268 |
} |
} |
|
|
|
|
/* Replace current standard output/error fd's with new ones */ |
|
|
debug("duping\n"); |
|
|
xdup2(0, 1, tty); |
|
|
xdup2(0, 2, tty); |
|
|
|
|
|
/* |
|
|
* The following ioctl will fail if stdin is not a tty, but also when |
|
|
* there is noise on the modem control lines. In the latter case, the |
|
|
* common course of action is (1) fix your cables (2) give the modem more |
|
|
* time to properly reset after hanging up. SunOS users can achieve (2) |
|
|
* by patching the SunOS kernel variable "zsadtrlow" to a larger value; |
|
|
* 5 seconds seems to be a good value. |
|
|
*/ |
|
|
|
|
|
if (ioctl(0, TCGETS, tp) < 0) |
|
|
bb_perror_msg_and_die("%s: ioctl(TCGETS)", tty); |
|
|
|
|
|
/* |
|
|
* It seems to be a terminal. Set proper protections and ownership. Mode |
|
|
* 0622 is suitable for SYSV <4 because /bin/login does not change |
|
|
* protections. SunOS 4 login will change the protections to 0620 (write |
|
|
* access for group tty) after the login has succeeded. |
|
|
*/ |
|
|
|
|
|
#ifdef DEBIAN |
|
|
#warning Debian /dev/vcs[a]NN hack is deprecated and will be removed |
|
|
{ |
|
|
/* tty to root.dialout 660 */ |
|
|
struct group *gr; |
|
|
int id; |
|
|
|
|
|
gr = getgrnam("dialout"); |
|
|
id = gr ? gr->gr_gid : 0; |
|
|
chown(tty, 0, id); |
|
|
chmod(tty, 0660); |
|
|
|
|
|
/* vcs,vcsa to root.sys 600 */ |
|
|
if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) { |
|
|
char *vcs, *vcsa; |
|
|
|
|
|
vcs = xstrdup(tty); |
|
|
vcsa = xmalloc(strlen(tty) + 2); |
|
|
strcpy(vcs, "vcs"); |
|
|
strcpy(vcs + 3, tty + 3); |
|
|
strcpy(vcsa, "vcsa"); |
|
|
strcpy(vcsa + 4, tty + 3); |
|
|
|
|
|
id = (gr = getgrnam("sys")) ? gr->gr_gid : 0; |
|
|
chown(vcs, 0, id); |
|
|
chmod(vcs, 0600); |
|
|
chown(vcsa, 0, id); |
|
|
chmod(vcs, 0600); |
|
|
|
|
|
free(vcs); |
|
|
free(vcsa); |
|
|
} |
|
|
} |
|
|
#else |
|
|
if (NOT_LONE_DASH(tty)) { |
|
|
chown(tty, 0, 0); /* 0:0 */ |
|
|
chmod(tty, 0622); /* crw--w--w- */ |
|
|
} |
|
|
#endif |
|
|
if (chdir_to_root) |
|
|
xchdir("/"); |
|
269 |
} |
} |
270 |
|
|
271 |
/* termios_init - initialize termios settings */ |
/* termios_init - initialize termios settings */ |
279 |
*/ |
*/ |
280 |
#ifdef __linux__ |
#ifdef __linux__ |
281 |
/* flush input and output queues, important for modems! */ |
/* flush input and output queues, important for modems! */ |
282 |
ioctl(0, TCFLSH, TCIOFLUSH); |
ioctl(0, TCFLSH, TCIOFLUSH); /* tcflush(0, TCIOFLUSH)? - same */ |
283 |
#endif |
#endif |
284 |
|
|
285 |
tp->c_cflag = CS8 | HUPCL | CREAD | speed; |
tp->c_cflag = CS8 | HUPCL | CREAD | speed; |
286 |
if (op->flags & F_LOCAL) { |
if (op->flags & F_LOCAL) |
287 |
tp->c_cflag |= CLOCAL; |
tp->c_cflag |= CLOCAL; |
|
} |
|
288 |
|
|
289 |
tp->c_iflag = tp->c_lflag = tp->c_line = 0; |
tp->c_iflag = tp->c_lflag = tp->c_line = 0; |
290 |
tp->c_oflag = OPOST | ONLCR; |
tp->c_oflag = OPOST | ONLCR; |
292 |
tp->c_cc[VTIME] = 0; |
tp->c_cc[VTIME] = 0; |
293 |
|
|
294 |
/* Optionally enable hardware flow control */ |
/* Optionally enable hardware flow control */ |
295 |
|
#ifdef CRTSCTS |
|
#ifdef CRTSCTS |
|
296 |
if (op->flags & F_RTSCTS) |
if (op->flags & F_RTSCTS) |
297 |
tp->c_cflag |= CRTSCTS; |
tp->c_cflag |= CRTSCTS; |
298 |
#endif |
#endif |
299 |
|
|
300 |
ioctl(0, TCSETS, tp); |
tcsetattr_stdin_TCSANOW(tp); |
|
|
|
|
/* go to blocking input even in local mode */ |
|
|
fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK); |
|
301 |
|
|
302 |
debug("term_io 2\n"); |
debug("term_io 2\n"); |
303 |
} |
} |
330 |
* Use 7-bit characters, don't block if input queue is empty. Errors will |
* Use 7-bit characters, don't block if input queue is empty. Errors will |
331 |
* be dealt with later on. |
* be dealt with later on. |
332 |
*/ |
*/ |
|
|
|
333 |
iflag = tp->c_iflag; |
iflag = tp->c_iflag; |
334 |
tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */ |
tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */ |
335 |
vmin = tp->c_cc[VMIN]; |
vmin = tp->c_cc[VMIN]; |
336 |
tp->c_cc[VMIN] = 0; /* don't block if queue empty */ |
tp->c_cc[VMIN] = 0; /* don't block if queue empty */ |
337 |
ioctl(0, TCSETS, tp); |
tcsetattr_stdin_TCSANOW(tp); |
338 |
|
|
339 |
/* |
/* |
340 |
* Wait for a while, then read everything the modem has said so far and |
* Wait for a while, then read everything the modem has said so far and |
341 |
* try to extract the speed of the dial-in call. |
* try to extract the speed of the dial-in call. |
342 |
*/ |
*/ |
|
|
|
343 |
sleep(1); |
sleep(1); |
344 |
nread = read(0, buf, size_buf - 1); |
nread = safe_read(STDIN_FILENO, buf, size_buf - 1); |
345 |
if (nread > 0) { |
if (nread > 0) { |
346 |
buf[nread] = '\0'; |
buf[nread] = '\0'; |
347 |
for (bp = buf; bp < buf + nread; bp++) { |
for (bp = buf; bp < buf + nread; bp++) { |
348 |
if (isascii(*bp) && isdigit(*bp)) { |
if (isdigit(*bp)) { |
349 |
speed = bcode(bp); |
speed = bcode(bp); |
350 |
if (speed) { |
if (speed > 0) { |
351 |
tp->c_cflag &= ~CBAUD; |
tp->c_cflag &= ~CBAUD; |
352 |
tp->c_cflag |= speed; |
tp->c_cflag |= speed; |
353 |
} |
} |
355 |
} |
} |
356 |
} |
} |
357 |
} |
} |
|
/* Restore terminal settings. Errors will be dealt with later on. */ |
|
358 |
|
|
359 |
|
/* Restore terminal settings. Errors will be dealt with later on. */ |
360 |
tp->c_iflag = iflag; |
tp->c_iflag = iflag; |
361 |
tp->c_cc[VMIN] = vmin; |
tp->c_cc[VMIN] = vmin; |
362 |
ioctl(0, TCSETS, tp); |
tcsetattr_stdin_TCSANOW(tp); |
|
} |
|
|
|
|
|
/* next_speed - select next baud rate */ |
|
|
static void next_speed(struct termios *tp, struct options *op) |
|
|
{ |
|
|
static int baud_index = FIRST_SPEED; /* current speed index */ |
|
|
|
|
|
baud_index = (baud_index + 1) % op->numspeed; |
|
|
tp->c_cflag &= ~CBAUD; |
|
|
tp->c_cflag |= op->speeds[baud_index]; |
|
|
ioctl(0, TCSETS, tp); |
|
363 |
} |
} |
364 |
|
|
|
|
|
365 |
/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ |
/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ |
366 |
static void do_prompt(struct options *op, struct termios *tp) |
static void do_prompt(struct options *op) |
367 |
{ |
{ |
368 |
#ifdef ISSUE |
#ifdef ISSUE |
369 |
print_login_issue(op->issue, op->tty); |
print_login_issue(op->issue, op->tty); |
372 |
} |
} |
373 |
|
|
374 |
#ifdef HANDLE_ALLCAPS |
#ifdef HANDLE_ALLCAPS |
375 |
/* caps_lock - string contains upper case without lower case */ |
/* all_is_upcase - string contains upper case without lower case */ |
376 |
/* returns 1 if true, 0 if false */ |
/* returns 1 if true, 0 if false */ |
377 |
static int caps_lock(const char *s) |
static int all_is_upcase(const char *s) |
378 |
{ |
{ |
379 |
while (*s) |
while (*s) |
380 |
if (islower(*s++)) |
if (islower(*s++)) |
383 |
} |
} |
384 |
#endif |
#endif |
385 |
|
|
386 |
/* get_logname - get user name, establish parity, speed, erase, kill, eol */ |
/* get_logname - get user name, establish parity, speed, erase, kill, eol; |
387 |
/* return NULL on failure, logname on success */ |
* return NULL on BREAK, logname on success */ |
388 |
static char *get_logname(char *logname, unsigned size_logname, |
static char *get_logname(char *logname, unsigned size_logname, |
389 |
struct options *op, struct chardata *cp, struct termios *tp) |
struct options *op, struct chardata *cp) |
390 |
{ |
{ |
391 |
char *bp; |
char *bp; |
392 |
char c; /* input character, full eight bits */ |
char c; /* input character, full eight bits */ |
393 |
char ascval; /* low 7 bits of input character */ |
char ascval; /* low 7 bits of input character */ |
394 |
int bits; /* # of "1" bits per character */ |
int bits; /* # of "1" bits per character */ |
395 |
int mask; /* mask with 1 bit up */ |
int mask; /* mask with 1 bit up */ |
396 |
static const char erase[][3] = { /* backspace-space-backspace */ |
static const char erase[][3] = {/* backspace-space-backspace */ |
397 |
"\010\040\010", /* space parity */ |
"\010\040\010", /* space parity */ |
398 |
"\010\040\010", /* odd parity */ |
"\010\040\010", /* odd parity */ |
399 |
"\210\240\210", /* even parity */ |
"\210\240\210", /* even parity */ |
400 |
"\210\240\210", /* no parity */ |
"\010\040\010", /* 8 bit no parity */ |
401 |
}; |
}; |
402 |
|
|
403 |
/* Initialize kill, erase, parity etc. (also after switching speeds). */ |
/* NB: *cp is pre-initialized with init_chardata */ |
|
|
|
|
*cp = init_chardata; |
|
404 |
|
|
405 |
/* Flush pending input (esp. after parsing or switching the baud rate). */ |
/* Flush pending input (esp. after parsing or switching the baud rate). */ |
|
|
|
406 |
sleep(1); |
sleep(1); |
407 |
ioctl(0, TCFLSH, TCIFLUSH); |
ioctl(0, TCFLSH, TCIFLUSH); /* tcflush(0, TCIOFLUSH)? - same */ |
408 |
|
|
409 |
/* Prompt for and read a login name. */ |
/* Prompt for and read a login name. */ |
|
|
|
410 |
logname[0] = '\0'; |
logname[0] = '\0'; |
411 |
while (!logname[0]) { |
while (!logname[0]) { |
|
|
|
412 |
/* Write issue file and prompt, with "parity" bit == 0. */ |
/* Write issue file and prompt, with "parity" bit == 0. */ |
413 |
|
do_prompt(op); |
|
do_prompt(op, tp); |
|
414 |
|
|
415 |
/* Read name, watch for break, parity, erase, kill, end-of-line. */ |
/* Read name, watch for break, parity, erase, kill, end-of-line. */ |
|
|
|
416 |
bp = logname; |
bp = logname; |
417 |
cp->eol = '\0'; |
cp->eol = '\0'; |
418 |
while (cp->eol == '\0') { |
while (cp->eol == '\0') { |
419 |
|
|
420 |
/* Do not report trivial EINTR/EIO errors. */ |
/* Do not report trivial EINTR/EIO errors. */ |
421 |
if (read(0, &c, 1) < 1) { |
if (read(STDIN_FILENO, &c, 1) < 1) { |
422 |
if (errno == EINTR || errno == EIO) |
if (errno == EINTR || errno == EIO) |
423 |
exit(0); |
exit(EXIT_SUCCESS); |
424 |
bb_perror_msg_and_die("%s: read", op->tty); |
bb_perror_msg_and_die("%s: read", op->tty); |
425 |
} |
} |
426 |
|
|
427 |
/* Do BREAK handling elsewhere. */ |
/* BREAK. If we have speeds to try, |
428 |
|
* return NULL (will switch speeds and return here) */ |
429 |
if (c == '\0' && op->numspeed > 1) |
if (c == '\0' && op->numspeed > 1) |
430 |
return NULL; |
return NULL; |
431 |
|
|
432 |
/* Do parity bit handling. */ |
/* Do parity bit handling. */ |
433 |
ascval = c & 0177; |
if (!(op->flags & F_LOCAL) && (c & 0x80)) { /* "parity" bit on? */ |
|
if (c != ascval) { /* "parity" bit on ? */ |
|
434 |
bits = 1; |
bits = 1; |
435 |
mask = 1; |
mask = 1; |
436 |
while (mask & 0177) { |
while (mask & 0x7f) { |
437 |
if (mask & ascval) |
if (mask & c) |
438 |
bits++; /* count "1" bits */ |
bits++; /* count "1" bits */ |
439 |
mask <<= 1; |
mask <<= 1; |
440 |
} |
} |
443 |
} |
} |
444 |
|
|
445 |
/* Do erase, kill and end-of-line processing. */ |
/* Do erase, kill and end-of-line processing. */ |
446 |
|
ascval = c & 0x7f; |
447 |
switch (ascval) { |
switch (ascval) { |
448 |
case CR: |
case CR: |
449 |
case NL: |
case NL: |
452 |
break; |
break; |
453 |
case BS: |
case BS: |
454 |
case DEL: |
case DEL: |
455 |
|
#ifdef ANCIENT_BS_KILL_CHARS |
456 |
case '#': |
case '#': |
457 |
|
#endif |
458 |
cp->erase = ascval; /* set erase character */ |
cp->erase = ascval; /* set erase character */ |
459 |
if (bp > logname) { |
if (bp > logname) { |
460 |
write(1, erase[cp->parity], 3); |
full_write(STDOUT_FILENO, erase[cp->parity], 3); |
461 |
bp--; |
bp--; |
462 |
} |
} |
463 |
break; |
break; |
464 |
case CTL('U'): |
case CTL('U'): |
465 |
|
#ifdef ANCIENT_BS_KILL_CHARS |
466 |
case '@': |
case '@': |
467 |
|
#endif |
468 |
cp->kill = ascval; /* set kill character */ |
cp->kill = ascval; /* set kill character */ |
469 |
while (bp > logname) { |
while (bp > logname) { |
470 |
write(1, erase[cp->parity], 3); |
full_write(STDOUT_FILENO, erase[cp->parity], 3); |
471 |
bp--; |
bp--; |
472 |
} |
} |
473 |
break; |
break; |
474 |
case CTL('D'): |
case CTL('D'): |
475 |
exit(0); |
exit(EXIT_SUCCESS); |
476 |
default: |
default: |
477 |
if (!isascii(ascval) || !isprint(ascval)) { |
if (!isascii(ascval) || !isprint(ascval)) { |
478 |
/* ignore garbage characters */ |
/* ignore garbage characters */ |
479 |
} else if (bp - logname >= size_logname - 1) { |
} else if ((int)(bp - logname) >= size_logname - 1) { |
480 |
bb_error_msg_and_die("%s: input overrun", op->tty); |
bb_error_msg_and_die("%s: input overrun", op->tty); |
481 |
} else { |
} else { |
482 |
write(1, &c, 1); /* echo the character */ |
full_write(STDOUT_FILENO, &c, 1); /* echo the character */ |
483 |
*bp++ = ascval; /* and store it */ |
*bp++ = ascval; /* and store it */ |
484 |
} |
} |
485 |
break; |
break; |
489 |
/* Handle names with upper case and no lower case. */ |
/* Handle names with upper case and no lower case. */ |
490 |
|
|
491 |
#ifdef HANDLE_ALLCAPS |
#ifdef HANDLE_ALLCAPS |
492 |
cp->capslock = caps_lock(logname); |
cp->capslock = all_is_upcase(logname); |
493 |
if (cp->capslock) { |
if (cp->capslock) { |
494 |
for (bp = logname; *bp; bp++) |
for (bp = logname; *bp; bp++) |
495 |
if (isupper(*bp)) |
if (isupper(*bp)) |
503 |
static void termios_final(struct options *op, struct termios *tp, struct chardata *cp) |
static void termios_final(struct options *op, struct termios *tp, struct chardata *cp) |
504 |
{ |
{ |
505 |
/* General terminal-independent stuff. */ |
/* General terminal-independent stuff. */ |
|
|
|
506 |
tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */ |
tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */ |
507 |
tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; |
tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; |
508 |
/* no longer| ECHOCTL | ECHOPRT */ |
/* no longer| ECHOCTL | ECHOPRT */ |
515 |
tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */ |
tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */ |
516 |
|
|
517 |
/* Account for special characters seen in input. */ |
/* Account for special characters seen in input. */ |
|
|
|
518 |
if (cp->eol == CR) { |
if (cp->eol == CR) { |
519 |
tp->c_iflag |= ICRNL; /* map CR in input to NL */ |
tp->c_iflag |= ICRNL; /* map CR in input to NL */ |
520 |
tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */ |
tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */ |
523 |
tp->c_cc[VKILL] = cp->kill; /* set kill character */ |
tp->c_cc[VKILL] = cp->kill; /* set kill character */ |
524 |
|
|
525 |
/* Account for the presence or absence of parity bits in input. */ |
/* Account for the presence or absence of parity bits in input. */ |
|
|
|
526 |
switch (cp->parity) { |
switch (cp->parity) { |
527 |
case 0: /* space (always 0) parity */ |
case 0: /* space (always 0) parity */ |
528 |
|
// I bet most people go here - they use only 7-bit chars in usernames.... |
529 |
break; |
break; |
530 |
case 1: /* odd parity */ |
case 1: /* odd parity */ |
531 |
tp->c_cflag |= PARODD; |
tp->c_cflag |= PARODD; |
537 |
case (1 | 2): /* no parity bit */ |
case (1 | 2): /* no parity bit */ |
538 |
tp->c_cflag &= ~CSIZE; |
tp->c_cflag &= ~CSIZE; |
539 |
tp->c_cflag |= CS7; |
tp->c_cflag |= CS7; |
540 |
|
// FIXME: wtf? case 3: we saw both even and odd 8-bit bytes - |
541 |
|
// it's probably some umlauts etc, but definitely NOT 7-bit!!! |
542 |
|
// Entire parity detection madness here just begs for deletion... |
543 |
break; |
break; |
544 |
} |
} |
|
/* Account for upper case without lower case. */ |
|
545 |
|
|
546 |
|
/* Account for upper case without lower case. */ |
547 |
#ifdef HANDLE_ALLCAPS |
#ifdef HANDLE_ALLCAPS |
548 |
if (cp->capslock) { |
if (cp->capslock) { |
549 |
tp->c_iflag |= IUCLC; |
tp->c_iflag |= IUCLC; |
552 |
} |
} |
553 |
#endif |
#endif |
554 |
/* Optionally enable hardware flow control */ |
/* Optionally enable hardware flow control */ |
555 |
|
#ifdef CRTSCTS |
|
#ifdef CRTSCTS |
|
556 |
if (op->flags & F_RTSCTS) |
if (op->flags & F_RTSCTS) |
557 |
tp->c_cflag |= CRTSCTS; |
tp->c_cflag |= CRTSCTS; |
558 |
#endif |
#endif |
559 |
|
|
560 |
/* Finally, make the new settings effective */ |
/* Finally, make the new settings effective */ |
561 |
|
/* It's tcsetattr_stdin_TCSANOW() + error check */ |
562 |
if (ioctl(0, TCSETS, tp) < 0) |
ioctl_or_perror_and_die(0, TCSETS, tp, "%s: TCSETS", op->tty); |
|
bb_perror_msg_and_die("%s: ioctl(TCSETS)", op->tty); |
|
563 |
} |
} |
564 |
|
|
|
|
|
|
#ifdef SYSV_STYLE |
|
565 |
#if ENABLE_FEATURE_UTMP |
#if ENABLE_FEATURE_UTMP |
566 |
|
static void touch(const char *filename) |
567 |
|
{ |
568 |
|
if (access(filename, R_OK | W_OK) == -1) |
569 |
|
close(open(filename, O_WRONLY | O_CREAT, 0664)); |
570 |
|
} |
571 |
|
|
572 |
/* update_utmp - update our utmp entry */ |
/* update_utmp - update our utmp entry */ |
573 |
static void update_utmp(char *line) |
static void update_utmp(const char *line, char *fakehost) |
574 |
{ |
{ |
575 |
struct utmp ut; |
struct utmp ut; |
576 |
struct utmp *utp; |
struct utmp *utp; |
|
time_t t; |
|
577 |
int mypid = getpid(); |
int mypid = getpid(); |
578 |
|
|
579 |
|
/* In case we won't find an entry below... */ |
580 |
|
memset(&ut, 0, sizeof(ut)); |
581 |
|
safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); |
582 |
|
|
583 |
/* |
/* |
584 |
* The utmp file holds miscellaneous information about things started by |
* The utmp file holds miscellaneous information about things started by |
585 |
* /sbin/init and other system-related events. Our purpose is to update |
* /sbin/init and other system-related events. Our purpose is to update |
588 |
* utmp file can be opened for update, and if we are able to find our |
* utmp file can be opened for update, and if we are able to find our |
589 |
* entry in the utmp file. |
* entry in the utmp file. |
590 |
*/ |
*/ |
591 |
if (access(_PATH_UTMP, R_OK|W_OK) == -1) { |
touch(_PATH_UTMP); |
592 |
close(creat(_PATH_UTMP, 0664)); |
|
|
} |
|
593 |
utmpname(_PATH_UTMP); |
utmpname(_PATH_UTMP); |
594 |
setutent(); |
setutent(); |
595 |
while ((utp = getutent()) |
while ((utp = getutent()) != NULL) { |
596 |
&& !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) |
if (utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid) { |
597 |
/* nothing */; |
memcpy(&ut, utp, sizeof(ut)); |
598 |
|
break; |
599 |
if (utp) { |
} |
|
memcpy(&ut, utp, sizeof(ut)); |
|
|
} else { |
|
|
/* some inits don't initialize utmp... */ |
|
|
memset(&ut, 0, sizeof(ut)); |
|
|
safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); |
|
600 |
} |
} |
|
/* endutent(); */ |
|
601 |
|
|
602 |
strcpy(ut.ut_user, "LOGIN"); |
strcpy(ut.ut_user, "LOGIN"); |
603 |
safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line)); |
safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line)); |
604 |
if (fakehost) |
if (fakehost) |
605 |
safe_strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host)); |
safe_strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host)); |
606 |
time(&t); |
ut.ut_tv.tv_sec = time(NULL); |
|
ut.ut_time = t; |
|
607 |
ut.ut_type = LOGIN_PROCESS; |
ut.ut_type = LOGIN_PROCESS; |
608 |
ut.ut_pid = mypid; |
ut.ut_pid = mypid; |
609 |
|
|
611 |
endutent(); |
endutent(); |
612 |
|
|
613 |
#if ENABLE_FEATURE_WTMP |
#if ENABLE_FEATURE_WTMP |
614 |
if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) |
touch(bb_path_wtmp_file); |
|
close(creat(bb_path_wtmp_file, 0664)); |
|
615 |
updwtmp(bb_path_wtmp_file, &ut); |
updwtmp(bb_path_wtmp_file, &ut); |
616 |
#endif |
#endif |
617 |
} |
} |
|
|
|
618 |
#endif /* CONFIG_FEATURE_UTMP */ |
#endif /* CONFIG_FEATURE_UTMP */ |
|
#endif /* SYSV_STYLE */ |
|
619 |
|
|
620 |
|
int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
621 |
int getty_main(int argc, char **argv) |
int getty_main(int argc UNUSED_PARAM, char **argv) |
622 |
{ |
{ |
623 |
int nullfd; |
int n; |
624 |
char *logname = NULL; /* login name, given to /bin/login */ |
char *fakehost = NULL; /* Fake hostname for ut_host */ |
625 |
|
char *logname; /* login name, given to /bin/login */ |
626 |
/* Merging these into "struct local" may _seem_ to reduce |
/* Merging these into "struct local" may _seem_ to reduce |
627 |
* parameter passing, but today's gcc will inline |
* parameter passing, but today's gcc will inline |
628 |
* statics which are called once anyway, so don't do that */ |
* statics which are called once anyway, so don't do that */ |
629 |
struct chardata chardata; /* set by get_logname() */ |
struct chardata chardata; /* set by get_logname() */ |
630 |
struct termios termios; /* terminal mode bits */ |
struct termios termios; /* terminal mode bits */ |
631 |
struct options options = { |
struct options options; |
632 |
0, /* show /etc/issue (SYSV_STYLE) */ |
|
633 |
0, /* no timeout */ |
chardata = init_chardata; |
634 |
_PATH_LOGIN, /* default login program */ |
|
635 |
"tty1", /* default tty line */ |
memset(&options, 0, sizeof(options)); |
636 |
"", /* modem init string */ |
options.login = _PATH_LOGIN; /* default login program */ |
637 |
|
options.tty = "tty1"; /* default tty line */ |
638 |
|
options.initstring = ""; /* modem init string */ |
639 |
#ifdef ISSUE |
#ifdef ISSUE |
640 |
ISSUE, /* default issue file */ |
options.issue = ISSUE; /* default issue file */ |
|
#else |
|
|
NULL, |
|
641 |
#endif |
#endif |
|
0, /* no baud rates known yet */ |
|
|
}; |
|
642 |
|
|
643 |
/* Already too late because of theoretical |
/* Parse command-line arguments. */ |
644 |
* possibility of getty --help somehow triggered |
parse_args(argv, &options, &fakehost); |
645 |
* inadvertently before we reach this. Oh well. */ |
|
646 |
logmode = LOGMODE_NONE; |
logmode = LOGMODE_NONE; |
647 |
|
|
648 |
|
/* Create new session, lose controlling tty, if any */ |
649 |
|
/* docs/ctty.htm says: |
650 |
|
* "This is allowed only when the current process |
651 |
|
* is not a process group leader" - is this a problem? */ |
652 |
setsid(); |
setsid(); |
653 |
nullfd = xopen(bb_dev_null, O_RDWR); |
/* close stdio, and stray descriptors, just in case */ |
654 |
/* dup2(nullfd, 0); - no, because of possible "getty - 9600" */ |
n = xopen(bb_dev_null, O_RDWR); |
655 |
/* open_tty() will take care of fd# 0 anyway */ |
/* dup2(n, 0); - no, we need to handle "getty - 9600" too */ |
656 |
dup2(nullfd, 1); |
xdup2(n, 1); |
657 |
dup2(nullfd, 2); |
xdup2(n, 2); |
658 |
while (nullfd > 2) close(nullfd--); |
while (n > 2) |
659 |
/* We want special flavor of error_msg_and_die */ |
close(n--); |
660 |
|
|
661 |
|
/* Logging. We want special flavor of error_msg_and_die */ |
662 |
die_sleep = 10; |
die_sleep = 10; |
663 |
msg_eol = "\r\n"; |
msg_eol = "\r\n"; |
664 |
|
/* most likely will internally use fd #3 in CLOEXEC mode: */ |
665 |
openlog(applet_name, LOG_PID, LOG_AUTH); |
openlog(applet_name, LOG_PID, LOG_AUTH); |
666 |
logmode = LOGMODE_BOTH; |
logmode = LOGMODE_BOTH; |
667 |
|
|
668 |
#ifdef DEBUGGING |
#ifdef DEBUGGING |
669 |
dbf = xfopen(DEBUGTERM, "w"); |
dbf = xfopen_for_write(DEBUGTERM); |
670 |
|
for (n = 1; argv[n]; n++) { |
671 |
{ |
debug(argv[n]); |
672 |
int i; |
debug("\n"); |
|
|
|
|
for (i = 1; i < argc; i++) { |
|
|
debug(argv[i]); |
|
|
debug("\n"); |
|
|
} |
|
673 |
} |
} |
674 |
#endif |
#endif |
675 |
|
|
676 |
/* Parse command-line arguments. */ |
/* Open the tty as standard input, if it is not "-" */ |
677 |
parse_args(argc, argv, &options); |
/* If it's not "-" and not taken yet, it will become our ctty */ |
|
|
|
|
#ifdef SYSV_STYLE |
|
|
#if ENABLE_FEATURE_UTMP |
|
|
/* Update the utmp file. */ |
|
|
update_utmp(options.tty); |
|
|
#endif |
|
|
#endif |
|
|
|
|
678 |
debug("calling open_tty\n"); |
debug("calling open_tty\n"); |
679 |
/* Open the tty as standard { input, output, error }. */ |
open_tty(options.tty); |
680 |
open_tty(options.tty, &termios, options.flags & F_LOCAL); |
ndelay_off(0); |
681 |
|
debug("duping\n"); |
682 |
|
xdup2(0, 1); |
683 |
|
xdup2(0, 2); |
684 |
|
|
685 |
|
/* |
686 |
|
* The following ioctl will fail if stdin is not a tty, but also when |
687 |
|
* there is noise on the modem control lines. In the latter case, the |
688 |
|
* common course of action is (1) fix your cables (2) give the modem more |
689 |
|
* time to properly reset after hanging up. SunOS users can achieve (2) |
690 |
|
* by patching the SunOS kernel variable "zsadtrlow" to a larger value; |
691 |
|
* 5 seconds seems to be a good value. |
692 |
|
*/ |
693 |
|
/* tcgetattr() + error check */ |
694 |
|
ioctl_or_perror_and_die(0, TCGETS, &termios, "%s: TCGETS", options.tty); |
695 |
|
|
696 |
#ifdef __linux__ |
#ifdef __linux__ |
697 |
{ |
// FIXME: do we need this? Otherwise "-" case seems to be broken... |
698 |
int iv; |
// /* Forcibly make fd 0 our controlling tty, even if another session |
699 |
|
// * has it as a ctty. (Another session loses ctty). */ |
700 |
|
// ioctl(0, TIOCSCTTY, (void*)1); |
701 |
|
/* Make ourself a foreground process group within our session */ |
702 |
|
tcsetpgrp(0, getpid()); |
703 |
|
#endif |
704 |
|
|
705 |
iv = getpid(); |
#if ENABLE_FEATURE_UTMP |
706 |
ioctl(0, TIOCSPGRP, &iv); |
/* Update the utmp file. This tty is ours now! */ |
707 |
} |
update_utmp(options.tty, fakehost); |
708 |
#endif |
#endif |
709 |
|
|
710 |
/* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ |
/* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */ |
711 |
debug("calling termios_init\n"); |
debug("calling termios_init\n"); |
712 |
termios_init(&termios, options.speeds[FIRST_SPEED], &options); |
termios_init(&termios, options.speeds[0], &options); |
713 |
|
|
714 |
/* write the modem init string and DON'T flush the buffers */ |
/* Write the modem init string and DON'T flush the buffers */ |
715 |
if (options.flags & F_INITSTRING) { |
if (options.flags & F_INITSTRING) { |
716 |
debug("writing init string\n"); |
debug("writing init string\n"); |
717 |
write(1, options.initstring, strlen(options.initstring)); |
full_write(STDOUT_FILENO, options.initstring, strlen(options.initstring)); |
|
} |
|
|
|
|
|
if (!(options.flags & F_LOCAL)) { |
|
|
/* go to blocking write mode unless -L is specified */ |
|
|
fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK); |
|
718 |
} |
} |
719 |
|
|
720 |
/* Optionally detect the baud rate from the modem status message. */ |
/* Optionally detect the baud rate from the modem status message */ |
721 |
debug("before autobaud\n"); |
debug("before autobaud\n"); |
722 |
if (options.flags & F_PARSE) |
if (options.flags & F_PARSE) |
723 |
auto_baud(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), &termios); |
auto_baud(line_buf, sizeof(line_buf), &termios); |
724 |
|
|
725 |
/* Set the optional timer. */ |
/* Set the optional timer */ |
726 |
if (options.timeout) |
alarm(options.timeout); /* if 0, alarm is not set */ |
|
alarm(options.timeout); |
|
727 |
|
|
728 |
/* optionally wait for CR or LF before writing /etc/issue */ |
/* Optionally wait for CR or LF before writing /etc/issue */ |
729 |
if (options.flags & F_WAITCRLF) { |
if (options.flags & F_WAITCRLF) { |
730 |
char ch; |
char ch; |
731 |
|
|
732 |
debug("waiting for cr-lf\n"); |
debug("waiting for cr-lf\n"); |
733 |
while (read(0, &ch, 1) == 1) { |
while (safe_read(STDIN_FILENO, &ch, 1) == 1) { |
734 |
|
debug("read %x\n", (unsigned char)ch); |
735 |
ch &= 0x7f; /* strip "parity bit" */ |
ch &= 0x7f; /* strip "parity bit" */ |
|
#ifdef DEBUGGING |
|
|
fprintf(dbf, "read %c\n", ch); |
|
|
#endif |
|
736 |
if (ch == '\n' || ch == '\r') |
if (ch == '\n' || ch == '\r') |
737 |
break; |
break; |
738 |
} |
} |
739 |
} |
} |
740 |
|
|
741 |
chardata = init_chardata; |
logname = NULL; |
742 |
if (!(options.flags & F_NOPROMPT)) { |
if (!(options.flags & F_NOPROMPT)) { |
743 |
/* Read the login name. */ |
/* NB:termios_init already set line speed |
744 |
debug("reading login name\n"); |
* to options.speeds[0] */ |
745 |
logname = get_logname(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), |
int baud_index = 0; |
746 |
&options, &chardata, &termios); |
|
747 |
while (logname == NULL) |
while (1) { |
748 |
next_speed(&termios, &options); |
/* Read the login name. */ |
749 |
|
debug("reading login name\n"); |
750 |
|
logname = get_logname(line_buf, sizeof(line_buf), |
751 |
|
&options, &chardata); |
752 |
|
if (logname) |
753 |
|
break; |
754 |
|
/* we are here only if options.numspeed > 1 */ |
755 |
|
baud_index = (baud_index + 1) % options.numspeed; |
756 |
|
termios.c_cflag &= ~CBAUD; |
757 |
|
termios.c_cflag |= options.speeds[baud_index]; |
758 |
|
tcsetattr_stdin_TCSANOW(&termios); |
759 |
|
} |
760 |
} |
} |
761 |
|
|
762 |
/* Disable timer. */ |
/* Disable timer. */ |
763 |
|
alarm(0); |
|
if (options.timeout) |
|
|
alarm(0); |
|
764 |
|
|
765 |
/* Finalize the termios settings. */ |
/* Finalize the termios settings. */ |
|
|
|
766 |
termios_final(&options, &termios, &chardata); |
termios_final(&options, &termios, &chardata); |
767 |
|
|
768 |
/* Now the newline character should be properly written. */ |
/* Now the newline character should be properly written. */ |
769 |
|
full_write(STDOUT_FILENO, "\n", 1); |
|
write(1, "\n", 1); |
|
770 |
|
|
771 |
/* Let the login program take care of password validation. */ |
/* Let the login program take care of password validation. */ |
772 |
|
/* We use PATH because we trust that root doesn't set "bad" PATH, |
773 |
execl(options.login, options.login, "--", logname, (char *) 0); |
* and getty is not suid-root applet. */ |
774 |
|
/* With -n, logname == NULL, and login will ask for username instead */ |
775 |
|
BB_EXECLP(options.login, options.login, "--", logname, NULL); |
776 |
bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login); |
bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login); |
777 |
} |
} |