getty.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. /* vi: set sw=4 ts=4: */
  2. /* agetty.c - another getty program for Linux. By W. Z. Venema 1989
  3. * Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
  4. * This program is freely distributable. The entire man-page used to
  5. * be here. Now read the real man-page agetty.8 instead.
  6. *
  7. * option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
  8. *
  9. * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
  10. * - added Native Language Support
  11. *
  12. * 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
  13. * - enable hardware flow control before displaying /etc/issue
  14. *
  15. * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  16. */
  17. #include "libbb.h"
  18. #include <syslog.h>
  19. #if ENABLE_FEATURE_UTMP
  20. #include <utmp.h> /* updwtmp() */
  21. #endif
  22. #ifndef IUCLC
  23. # define IUCLC 0
  24. #endif
  25. /*
  26. * Some heuristics to find out what environment we are in: if it is not
  27. * System V, assume it is SunOS 4.
  28. */
  29. #ifdef LOGIN_PROCESS /* defined in System V utmp.h */
  30. #include <sys/utsname.h>
  31. #else /* if !sysV style, wtmp/utmp code is off */
  32. #undef ENABLE_FEATURE_UTMP
  33. #undef ENABLE_FEATURE_WTMP
  34. #define ENABLE_FEATURE_UTMP 0
  35. #define ENABLE_FEATURE_WTMP 0
  36. #endif /* LOGIN_PROCESS */
  37. /*
  38. * Things you may want to modify.
  39. *
  40. * You may disagree with the default line-editing etc. characters defined
  41. * below. Note, however, that DEL cannot be used for interrupt generation
  42. * and for line editing at the same time.
  43. */
  44. /* I doubt there are systems which still need this */
  45. #undef HANDLE_ALLCAPS
  46. #undef ANCIENT_BS_KILL_CHARS
  47. #define _PATH_LOGIN "/bin/login"
  48. /* If ISSUE is not defined, getty will never display the contents of the
  49. * /etc/issue file. You will not want to spit out large "issue" files at the
  50. * wrong baud rate.
  51. */
  52. #define ISSUE "/etc/issue" /* displayed before the login prompt */
  53. /* Some shorthands for control characters. */
  54. #define CTL(x) ((x) ^ 0100) /* Assumes ASCII dialect */
  55. #define CR CTL('M') /* carriage return */
  56. #define NL CTL('J') /* line feed */
  57. #define BS CTL('H') /* back space */
  58. #define DEL CTL('?') /* delete */
  59. /* Defaults for line-editing etc. characters; you may want to change this. */
  60. #define DEF_ERASE DEL /* default erase character */
  61. #define DEF_INTR CTL('C') /* default interrupt character */
  62. #define DEF_QUIT CTL('\\') /* default quit char */
  63. #define DEF_KILL CTL('U') /* default kill char */
  64. #define DEF_EOF CTL('D') /* default EOF char */
  65. #define DEF_EOL '\n'
  66. #define DEF_SWITCH 0 /* default switch char */
  67. /*
  68. * When multiple baud rates are specified on the command line, the first one
  69. * we will try is the first one specified.
  70. */
  71. #define MAX_SPEED 10 /* max. nr. of baud rates */
  72. /* Storage for command-line options. */
  73. struct options {
  74. int flags; /* toggle switches, see below */
  75. unsigned timeout; /* time-out period */
  76. const char *login; /* login program */
  77. const char *tty; /* name of tty */
  78. const char *initstring; /* modem init string */
  79. const char *issue; /* alternative issue file */
  80. int numspeed; /* number of baud rates to try */
  81. int speeds[MAX_SPEED]; /* baud rates to be tried */
  82. };
  83. /* Storage for things detected while the login name was read. */
  84. struct chardata {
  85. unsigned char erase; /* erase character */
  86. unsigned char kill; /* kill character */
  87. unsigned char eol; /* end-of-line character */
  88. unsigned char parity; /* what parity did we see */
  89. /* (parity & 1): saw odd parity char with 7th bit set */
  90. /* (parity & 2): saw even parity char with 7th bit set */
  91. /* parity == 0: probably 7-bit, space parity? */
  92. /* parity == 1: probably 7-bit, odd parity? */
  93. /* parity == 2: probably 7-bit, even parity? */
  94. /* parity == 3: definitely 8 bit, no parity! */
  95. /* Hmm... with any value of "parity" 8 bit, no parity is possible */
  96. #ifdef HANDLE_ALLCAPS
  97. unsigned char capslock; /* upper case without lower case */
  98. #endif
  99. };
  100. /* Initial values for the above. */
  101. static const struct chardata init_chardata = {
  102. DEF_ERASE, /* default erase character */
  103. DEF_KILL, /* default kill character */
  104. 13, /* default eol char */
  105. 0, /* space parity */
  106. #ifdef HANDLE_ALLCAPS
  107. 0, /* no capslock */
  108. #endif
  109. };
  110. static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn";
  111. #define F_INITSTRING (1 << 0) /* -I initstring is set */
  112. #define F_LOCAL (1 << 1) /* -L force local */
  113. #define F_FAKEHOST (1 << 2) /* -H fake hostname */
  114. #define F_CUSTISSUE (1 << 3) /* -f give alternative issue file */
  115. #define F_RTSCTS (1 << 4) /* -h enable RTS/CTS flow control */
  116. #define F_ISSUE (1 << 5) /* -i display /etc/issue */
  117. #define F_LOGIN (1 << 6) /* -l non-default login program */
  118. #define F_PARSE (1 << 7) /* -m process modem status messages */
  119. #define F_TIMEOUT (1 << 8) /* -t time out */
  120. #define F_WAITCRLF (1 << 9) /* -w wait for CR or LF */
  121. #define F_NOPROMPT (1 << 10) /* -n don't ask for login name */
  122. #define line_buf bb_common_bufsiz1
  123. /* The following is used for understandable diagnostics. */
  124. #ifdef DEBUGGING
  125. static FILE *dbf;
  126. #define DEBUGTERM "/dev/ttyp0"
  127. #define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0)
  128. #else
  129. #define debug(...) ((void)0)
  130. #endif
  131. /* bcode - convert speed string to speed code; return <= 0 on failure */
  132. static int bcode(const char *s)
  133. {
  134. int value = bb_strtou(s, NULL, 10); /* yes, int is intended! */
  135. if (value < 0) /* bad terminating char, overflow, etc */
  136. return value;
  137. return tty_value_to_baud(value);
  138. }
  139. /* parse_speeds - parse alternate baud rates */
  140. static void parse_speeds(struct options *op, char *arg)
  141. {
  142. char *cp;
  143. /* NB: at least one iteration is always done */
  144. debug("entered parse_speeds\n");
  145. while ((cp = strsep(&arg, ",")) != NULL) {
  146. op->speeds[op->numspeed] = bcode(cp);
  147. if (op->speeds[op->numspeed] < 0)
  148. bb_error_msg_and_die("bad speed: %s", cp);
  149. /* note: arg "0" turns into speed B0 */
  150. op->numspeed++;
  151. if (op->numspeed > MAX_SPEED)
  152. bb_error_msg_and_die("too many alternate speeds");
  153. }
  154. debug("exiting parse_speeds\n");
  155. }
  156. /* parse_args - parse command-line arguments */
  157. static void parse_args(char **argv, struct options *op, char **fakehost_p)
  158. {
  159. char *ts;
  160. opt_complementary = "-2:t+"; /* at least 2 args; -t N */
  161. op->flags = getopt32(argv, opt_string,
  162. &(op->initstring), fakehost_p, &(op->issue),
  163. &(op->login), &op->timeout);
  164. argv += optind;
  165. if (op->flags & F_INITSTRING) {
  166. const char *p = op->initstring;
  167. char *q;
  168. op->initstring = q = xstrdup(p);
  169. /* copy optarg into op->initstring decoding \ddd
  170. octal codes into chars */
  171. while (*p) {
  172. if (*p == '\\') {
  173. p++;
  174. *q++ = bb_process_escape_sequence(&p);
  175. } else {
  176. *q++ = *p++;
  177. }
  178. }
  179. *q = '\0';
  180. }
  181. op->flags ^= F_ISSUE; /* invert flag "show /etc/issue" */
  182. debug("after getopt\n");
  183. /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
  184. op->tty = argv[0]; /* tty name */
  185. ts = argv[1]; /* baud rate(s) */
  186. if (isdigit(argv[0][0])) {
  187. /* a number first, assume it's a speed (BSD style) */
  188. op->tty = ts; /* tty name is in argv[1] */
  189. ts = argv[0]; /* baud rate(s) */
  190. }
  191. parse_speeds(op, ts);
  192. // TODO: if applet_name is set to "getty: TTY", bb_error_msg's get simpler!
  193. // grep for "%s:"
  194. if (argv[2])
  195. xsetenv("TERM", argv[2]);
  196. debug("exiting parse_args\n");
  197. }
  198. /* open_tty - set up tty as standard { input, output, error } */
  199. static void open_tty(const char *tty)
  200. {
  201. /* Set up new standard input, unless we are given an already opened port. */
  202. if (NOT_LONE_DASH(tty)) {
  203. // struct stat st;
  204. // int cur_dir_fd;
  205. // int fd;
  206. /* Sanity checks... */
  207. // cur_dir_fd = xopen(".", O_DIRECTORY | O_NONBLOCK);
  208. // xchdir("/dev");
  209. // xstat(tty, &st);
  210. // if (!S_ISCHR(st.st_mode))
  211. // bb_error_msg_and_die("%s: not a character device", tty);
  212. if (tty[0] != '/')
  213. tty = xasprintf("/dev/%s", tty); /* will leak it */
  214. /* Open the tty as standard input. */
  215. debug("open(2)\n");
  216. close(0);
  217. /*fd =*/ xopen(tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */
  218. // /* Restore current directory */
  219. // fchdir(cur_dir_fd);
  220. /* Open the tty as standard input, continued */
  221. // xmove_fd(fd, 0);
  222. // /* fd is >= cur_dir_fd, and cur_dir_fd gets closed too here: */
  223. // while (fd > 2)
  224. // close(fd--);
  225. /* Set proper protections and ownership. */
  226. fchown(0, 0, 0); /* 0:0 */
  227. fchmod(0, 0620); /* crw--w---- */
  228. } else {
  229. /*
  230. * Standard input should already be connected to an open port. Make
  231. * sure it is open for read/write.
  232. */
  233. if ((fcntl(0, F_GETFL) & O_RDWR) != O_RDWR)
  234. bb_error_msg_and_die("stdin is not open for read/write");
  235. }
  236. }
  237. /* termios_init - initialize termios settings */
  238. static void termios_init(struct termios *tp, int speed, struct options *op)
  239. {
  240. speed_t ispeed, ospeed;
  241. /*
  242. * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
  243. * Special characters are set after we have read the login name; all
  244. * reads will be done in raw mode anyway. Errors will be dealt with
  245. * later on.
  246. */
  247. #ifdef __linux__
  248. /* flush input and output queues, important for modems! */
  249. ioctl(0, TCFLSH, TCIOFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
  250. #endif
  251. ispeed = ospeed = speed;
  252. if (speed == B0) {
  253. /* Speed was specified as "0" on command line.
  254. * Just leave it unchanged */
  255. ispeed = cfgetispeed(tp);
  256. ospeed = cfgetospeed(tp);
  257. }
  258. tp->c_cflag = CS8 | HUPCL | CREAD;
  259. if (op->flags & F_LOCAL)
  260. tp->c_cflag |= CLOCAL;
  261. cfsetispeed(tp, ispeed);
  262. cfsetospeed(tp, ospeed);
  263. tp->c_iflag = tp->c_lflag = tp->c_line = 0;
  264. tp->c_oflag = OPOST | ONLCR;
  265. tp->c_cc[VMIN] = 1;
  266. tp->c_cc[VTIME] = 0;
  267. /* Optionally enable hardware flow control */
  268. #ifdef CRTSCTS
  269. if (op->flags & F_RTSCTS)
  270. tp->c_cflag |= CRTSCTS;
  271. #endif
  272. tcsetattr_stdin_TCSANOW(tp);
  273. debug("term_io 2\n");
  274. }
  275. /* auto_baud - extract baud rate from modem status message */
  276. static void auto_baud(char *buf, unsigned size_buf, struct termios *tp)
  277. {
  278. int speed;
  279. int vmin;
  280. unsigned iflag;
  281. char *bp;
  282. int nread;
  283. /*
  284. * This works only if the modem produces its status code AFTER raising
  285. * the DCD line, and if the computer is fast enough to set the proper
  286. * baud rate before the message has gone by. We expect a message of the
  287. * following format:
  288. *
  289. * <junk><number><junk>
  290. *
  291. * The number is interpreted as the baud rate of the incoming call. If the
  292. * modem does not tell us the baud rate within one second, we will keep
  293. * using the current baud rate. It is advisable to enable BREAK
  294. * processing (comma-separated list of baud rates) if the processing of
  295. * modem status messages is enabled.
  296. */
  297. /*
  298. * Use 7-bit characters, don't block if input queue is empty. Errors will
  299. * be dealt with later on.
  300. */
  301. iflag = tp->c_iflag;
  302. tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */
  303. vmin = tp->c_cc[VMIN];
  304. tp->c_cc[VMIN] = 0; /* don't block if queue empty */
  305. tcsetattr_stdin_TCSANOW(tp);
  306. /*
  307. * Wait for a while, then read everything the modem has said so far and
  308. * try to extract the speed of the dial-in call.
  309. */
  310. sleep(1);
  311. nread = safe_read(STDIN_FILENO, buf, size_buf - 1);
  312. if (nread > 0) {
  313. buf[nread] = '\0';
  314. for (bp = buf; bp < buf + nread; bp++) {
  315. if (isdigit(*bp)) {
  316. speed = bcode(bp);
  317. if (speed > 0) {
  318. tp->c_cflag &= ~CBAUD;
  319. tp->c_cflag |= speed;
  320. }
  321. break;
  322. }
  323. }
  324. }
  325. /* Restore terminal settings. Errors will be dealt with later on. */
  326. tp->c_iflag = iflag;
  327. tp->c_cc[VMIN] = vmin;
  328. tcsetattr_stdin_TCSANOW(tp);
  329. }
  330. /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
  331. static void do_prompt(struct options *op)
  332. {
  333. #ifdef ISSUE
  334. print_login_issue(op->issue, op->tty);
  335. #endif
  336. print_login_prompt();
  337. }
  338. #ifdef HANDLE_ALLCAPS
  339. /* all_is_upcase - string contains upper case without lower case */
  340. /* returns 1 if true, 0 if false */
  341. static int all_is_upcase(const char *s)
  342. {
  343. while (*s)
  344. if (islower(*s++))
  345. return 0;
  346. return 1;
  347. }
  348. #endif
  349. /* get_logname - get user name, establish parity, speed, erase, kill, eol;
  350. * return NULL on BREAK, logname on success */
  351. static char *get_logname(char *logname, unsigned size_logname,
  352. struct options *op, struct chardata *cp)
  353. {
  354. char *bp;
  355. char c; /* input character, full eight bits */
  356. char ascval; /* low 7 bits of input character */
  357. int bits; /* # of "1" bits per character */
  358. int mask; /* mask with 1 bit up */
  359. static const char erase[][3] = {/* backspace-space-backspace */
  360. "\010\040\010", /* space parity */
  361. "\010\040\010", /* odd parity */
  362. "\210\240\210", /* even parity */
  363. "\010\040\010", /* 8 bit no parity */
  364. };
  365. /* NB: *cp is pre-initialized with init_chardata */
  366. /* Flush pending input (esp. after parsing or switching the baud rate). */
  367. sleep(1);
  368. ioctl(0, TCFLSH, TCIFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
  369. /* Prompt for and read a login name. */
  370. logname[0] = '\0';
  371. while (!logname[0]) {
  372. /* Write issue file and prompt, with "parity" bit == 0. */
  373. do_prompt(op);
  374. /* Read name, watch for break, parity, erase, kill, end-of-line. */
  375. bp = logname;
  376. cp->eol = '\0';
  377. while (cp->eol == '\0') {
  378. /* Do not report trivial EINTR/EIO errors. */
  379. if (read(STDIN_FILENO, &c, 1) < 1) {
  380. if (errno == EINTR || errno == EIO)
  381. exit(EXIT_SUCCESS);
  382. bb_perror_msg_and_die("%s: read", op->tty);
  383. }
  384. /* BREAK. If we have speeds to try,
  385. * return NULL (will switch speeds and return here) */
  386. if (c == '\0' && op->numspeed > 1)
  387. return NULL;
  388. /* Do parity bit handling. */
  389. if (!(op->flags & F_LOCAL) && (c & 0x80)) { /* "parity" bit on? */
  390. bits = 1;
  391. mask = 1;
  392. while (mask & 0x7f) {
  393. if (mask & c)
  394. bits++; /* count "1" bits */
  395. mask <<= 1;
  396. }
  397. /* ... |= 2 - even, 1 - odd */
  398. cp->parity |= 2 - (bits & 1);
  399. }
  400. /* Do erase, kill and end-of-line processing. */
  401. ascval = c & 0x7f;
  402. switch (ascval) {
  403. case CR:
  404. case NL:
  405. *bp = '\0'; /* terminate logname */
  406. cp->eol = ascval; /* set end-of-line char */
  407. break;
  408. case BS:
  409. case DEL:
  410. #ifdef ANCIENT_BS_KILL_CHARS
  411. case '#':
  412. #endif
  413. cp->erase = ascval; /* set erase character */
  414. if (bp > logname) {
  415. full_write(STDOUT_FILENO, erase[cp->parity], 3);
  416. bp--;
  417. }
  418. break;
  419. case CTL('U'):
  420. #ifdef ANCIENT_BS_KILL_CHARS
  421. case '@':
  422. #endif
  423. cp->kill = ascval; /* set kill character */
  424. while (bp > logname) {
  425. full_write(STDOUT_FILENO, erase[cp->parity], 3);
  426. bp--;
  427. }
  428. break;
  429. case CTL('D'):
  430. exit(EXIT_SUCCESS);
  431. default:
  432. if (!isprint(ascval)) {
  433. /* ignore garbage characters */
  434. } else if ((int)(bp - logname) >= size_logname - 1) {
  435. bb_error_msg_and_die("%s: input overrun", op->tty);
  436. } else {
  437. full_write(STDOUT_FILENO, &c, 1); /* echo the character */
  438. *bp++ = ascval; /* and store it */
  439. }
  440. break;
  441. }
  442. }
  443. }
  444. /* Handle names with upper case and no lower case. */
  445. #ifdef HANDLE_ALLCAPS
  446. cp->capslock = all_is_upcase(logname);
  447. if (cp->capslock) {
  448. for (bp = logname; *bp; bp++)
  449. if (isupper(*bp))
  450. *bp = tolower(*bp); /* map name to lower case */
  451. }
  452. #endif
  453. return logname;
  454. }
  455. /* termios_final - set the final tty mode bits */
  456. static void termios_final(struct options *op, struct termios *tp, struct chardata *cp)
  457. {
  458. /* General terminal-independent stuff. */
  459. tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */
  460. tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
  461. /* no longer| ECHOCTL | ECHOPRT */
  462. tp->c_oflag |= OPOST;
  463. /* tp->c_cflag = 0; */
  464. tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */
  465. tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */
  466. tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */
  467. tp->c_cc[VEOL] = DEF_EOL;
  468. tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */
  469. /* Account for special characters seen in input. */
  470. if (cp->eol == CR) {
  471. tp->c_iflag |= ICRNL; /* map CR in input to NL */
  472. tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */
  473. }
  474. tp->c_cc[VERASE] = cp->erase; /* set erase character */
  475. tp->c_cc[VKILL] = cp->kill; /* set kill character */
  476. /* Account for the presence or absence of parity bits in input. */
  477. switch (cp->parity) {
  478. case 0: /* space (always 0) parity */
  479. // I bet most people go here - they use only 7-bit chars in usernames....
  480. break;
  481. case 1: /* odd parity */
  482. tp->c_cflag |= PARODD;
  483. /* FALLTHROUGH */
  484. case 2: /* even parity */
  485. tp->c_cflag |= PARENB;
  486. tp->c_iflag |= INPCK | ISTRIP;
  487. /* FALLTHROUGH */
  488. case (1 | 2): /* no parity bit */
  489. tp->c_cflag &= ~CSIZE;
  490. tp->c_cflag |= CS7;
  491. // FIXME: wtf? case 3: we saw both even and odd 8-bit bytes -
  492. // it's probably some umlauts etc, but definitely NOT 7-bit!!!
  493. // Entire parity detection madness here just begs for deletion...
  494. break;
  495. }
  496. /* Account for upper case without lower case. */
  497. #ifdef HANDLE_ALLCAPS
  498. if (cp->capslock) {
  499. tp->c_iflag |= IUCLC;
  500. tp->c_lflag |= XCASE;
  501. tp->c_oflag |= OLCUC;
  502. }
  503. #endif
  504. /* Optionally enable hardware flow control */
  505. #ifdef CRTSCTS
  506. if (op->flags & F_RTSCTS)
  507. tp->c_cflag |= CRTSCTS;
  508. #endif
  509. /* Finally, make the new settings effective */
  510. /* It's tcsetattr_stdin_TCSANOW() + error check */
  511. ioctl_or_perror_and_die(0, TCSETS, tp, "%s: TCSETS", op->tty);
  512. }
  513. #if ENABLE_FEATURE_UTMP
  514. static void touch(const char *filename)
  515. {
  516. if (access(filename, R_OK | W_OK) == -1)
  517. close(open(filename, O_WRONLY | O_CREAT, 0664));
  518. }
  519. /* update_utmp - update our utmp entry */
  520. static void update_utmp(const char *line, char *fakehost)
  521. {
  522. struct utmp ut;
  523. struct utmp *utp;
  524. int mypid = getpid();
  525. /* In case we won't find an entry below... */
  526. memset(&ut, 0, sizeof(ut));
  527. safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
  528. /*
  529. * The utmp file holds miscellaneous information about things started by
  530. * /sbin/init and other system-related events. Our purpose is to update
  531. * the utmp entry for the current process, in particular the process type
  532. * and the tty line we are listening to. Return successfully only if the
  533. * utmp file can be opened for update, and if we are able to find our
  534. * entry in the utmp file.
  535. */
  536. touch(_PATH_UTMP);
  537. utmpname(_PATH_UTMP);
  538. setutent();
  539. while ((utp = getutent()) != NULL) {
  540. if (utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid) {
  541. memcpy(&ut, utp, sizeof(ut));
  542. break;
  543. }
  544. }
  545. strcpy(ut.ut_user, "LOGIN");
  546. safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line));
  547. if (fakehost)
  548. safe_strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
  549. ut.ut_tv.tv_sec = time(NULL);
  550. ut.ut_type = LOGIN_PROCESS;
  551. ut.ut_pid = mypid;
  552. pututline(&ut);
  553. endutent();
  554. #if ENABLE_FEATURE_WTMP
  555. touch(bb_path_wtmp_file);
  556. updwtmp(bb_path_wtmp_file, &ut);
  557. #endif
  558. }
  559. #endif /* CONFIG_FEATURE_UTMP */
  560. int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  561. int getty_main(int argc UNUSED_PARAM, char **argv)
  562. {
  563. int n;
  564. char *fakehost = NULL; /* Fake hostname for ut_host */
  565. char *logname; /* login name, given to /bin/login */
  566. /* Merging these into "struct local" may _seem_ to reduce
  567. * parameter passing, but today's gcc will inline
  568. * statics which are called once anyway, so don't do that */
  569. struct chardata chardata; /* set by get_logname() */
  570. struct termios termios; /* terminal mode bits */
  571. struct options options;
  572. chardata = init_chardata;
  573. memset(&options, 0, sizeof(options));
  574. options.login = _PATH_LOGIN; /* default login program */
  575. options.tty = "tty1"; /* default tty line */
  576. options.initstring = ""; /* modem init string */
  577. #ifdef ISSUE
  578. options.issue = ISSUE; /* default issue file */
  579. #endif
  580. /* Parse command-line arguments. */
  581. parse_args(argv, &options, &fakehost);
  582. logmode = LOGMODE_NONE;
  583. /* Create new session, lose controlling tty, if any */
  584. /* docs/ctty.htm says:
  585. * "This is allowed only when the current process
  586. * is not a process group leader" - is this a problem? */
  587. setsid();
  588. /* close stdio, and stray descriptors, just in case */
  589. n = xopen(bb_dev_null, O_RDWR);
  590. /* dup2(n, 0); - no, we need to handle "getty - 9600" too */
  591. xdup2(n, 1);
  592. xdup2(n, 2);
  593. while (n > 2)
  594. close(n--);
  595. /* Logging. We want special flavor of error_msg_and_die */
  596. die_sleep = 10;
  597. msg_eol = "\r\n";
  598. /* most likely will internally use fd #3 in CLOEXEC mode: */
  599. openlog(applet_name, LOG_PID, LOG_AUTH);
  600. logmode = LOGMODE_BOTH;
  601. #ifdef DEBUGGING
  602. dbf = xfopen_for_write(DEBUGTERM);
  603. for (n = 1; argv[n]; n++) {
  604. debug(argv[n]);
  605. debug("\n");
  606. }
  607. #endif
  608. /* Open the tty as standard input, if it is not "-" */
  609. /* If it's not "-" and not taken yet, it will become our ctty */
  610. debug("calling open_tty\n");
  611. open_tty(options.tty);
  612. ndelay_off(0);
  613. debug("duping\n");
  614. xdup2(0, 1);
  615. xdup2(0, 2);
  616. /*
  617. * The following ioctl will fail if stdin is not a tty, but also when
  618. * there is noise on the modem control lines. In the latter case, the
  619. * common course of action is (1) fix your cables (2) give the modem more
  620. * time to properly reset after hanging up. SunOS users can achieve (2)
  621. * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
  622. * 5 seconds seems to be a good value.
  623. */
  624. /* tcgetattr() + error check */
  625. ioctl_or_perror_and_die(0, TCGETS, &termios, "%s: TCGETS", options.tty);
  626. #ifdef __linux__
  627. // FIXME: do we need this? Otherwise "-" case seems to be broken...
  628. // /* Forcibly make fd 0 our controlling tty, even if another session
  629. // * has it as a ctty. (Another session loses ctty). */
  630. // ioctl(0, TIOCSCTTY, (void*)1);
  631. /* Make ourself a foreground process group within our session */
  632. tcsetpgrp(0, getpid());
  633. #endif
  634. #if ENABLE_FEATURE_UTMP
  635. /* Update the utmp file. This tty is ours now! */
  636. update_utmp(options.tty, fakehost);
  637. #endif
  638. /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
  639. debug("calling termios_init\n");
  640. termios_init(&termios, options.speeds[0], &options);
  641. /* Write the modem init string and DON'T flush the buffers */
  642. if (options.flags & F_INITSTRING) {
  643. debug("writing init string\n");
  644. /* todo: use xwrite_str? */
  645. full_write(STDOUT_FILENO, options.initstring, strlen(options.initstring));
  646. }
  647. /* Optionally detect the baud rate from the modem status message */
  648. debug("before autobaud\n");
  649. if (options.flags & F_PARSE)
  650. auto_baud(line_buf, sizeof(line_buf), &termios);
  651. /* Set the optional timer */
  652. alarm(options.timeout); /* if 0, alarm is not set */
  653. /* Optionally wait for CR or LF before writing /etc/issue */
  654. if (options.flags & F_WAITCRLF) {
  655. char ch;
  656. debug("waiting for cr-lf\n");
  657. while (safe_read(STDIN_FILENO, &ch, 1) == 1) {
  658. debug("read %x\n", (unsigned char)ch);
  659. ch &= 0x7f; /* strip "parity bit" */
  660. if (ch == '\n' || ch == '\r')
  661. break;
  662. }
  663. }
  664. logname = NULL;
  665. if (!(options.flags & F_NOPROMPT)) {
  666. /* NB:termios_init already set line speed
  667. * to options.speeds[0] */
  668. int baud_index = 0;
  669. while (1) {
  670. /* Read the login name. */
  671. debug("reading login name\n");
  672. logname = get_logname(line_buf, sizeof(line_buf),
  673. &options, &chardata);
  674. if (logname)
  675. break;
  676. /* we are here only if options.numspeed > 1 */
  677. baud_index = (baud_index + 1) % options.numspeed;
  678. cfsetispeed(&termios, options.speeds[baud_index]);
  679. cfsetospeed(&termios, options.speeds[baud_index]);
  680. tcsetattr_stdin_TCSANOW(&termios);
  681. }
  682. }
  683. /* Disable timer. */
  684. alarm(0);
  685. /* Finalize the termios settings. */
  686. termios_final(&options, &termios, &chardata);
  687. /* Now the newline character should be properly written. */
  688. full_write(STDOUT_FILENO, "\n", 1);
  689. /* Let the login program take care of password validation. */
  690. /* We use PATH because we trust that root doesn't set "bad" PATH,
  691. * and getty is not suid-root applet. */
  692. /* With -n, logname == NULL, and login will ask for username instead */
  693. BB_EXECLP(options.login, options.login, "--", logname, NULL);
  694. bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login);
  695. }