getty.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /* getty.c - A getty program to get controlling terminal.
  2. *
  3. * Copyright 2012 Sandeep Sharma <sandeep.jack2756@gamil.com>
  4. * Copyright 2013 Kyungwan Han <asura321@gmail.com>
  5. *
  6. * No Standard.
  7. USE_GETTY(NEWTOY(getty, "<2t#<0H:I:l:f:iwnmLh", TOYFLAG_SBIN))
  8. config GETTY
  9. bool "getty"
  10. default n
  11. help
  12. usage: getty [OPTIONS] BAUD_RATE[,BAUD_RATE]... TTY [TERMTYPE]
  13. Wait for a modem to dial into serial port, adjust baud rate, call login.
  14. -h Enable hardware RTS/CTS flow control
  15. -L Set CLOCAL (ignore Carrier Detect state)
  16. -m Get baud rate from modem's CONNECT status message
  17. -n Don't prompt for login name
  18. -w Wait for CR or LF before sending /etc/issue
  19. -i Don't display /etc/issue
  20. -f ISSUE_FILE Display ISSUE_FILE instead of /etc/issue
  21. -l LOGIN Invoke LOGIN instead of /bin/login
  22. -t SEC Terminate after SEC if no login name is read
  23. -I INITSTR Send INITSTR before anything else
  24. -H HOST Log HOST into the utmp file as the hostname
  25. */
  26. #define FOR_getty
  27. #include "toys.h"
  28. GLOBALS(
  29. char *f, *l, *I, *H;
  30. long t;
  31. char *tty_name, buff[128];
  32. int speeds[20], sc;
  33. struct termios termios;
  34. )
  35. #define CTL(x) ((x) ^ 0100)
  36. #define HOSTNAME_SIZE 32
  37. static void parse_speeds(char *sp)
  38. {
  39. char *ptr;
  40. TT.sc = 0;
  41. while ((ptr = strsep(&sp, ","))) {
  42. TT.speeds[TT.sc] = atolx_range(ptr, 0, INT_MAX);
  43. if (TT.speeds[TT.sc] < 0) perror_exit("bad speed %s", ptr);
  44. if (++TT.sc > 10) perror_exit("too many speeds, max is 10");
  45. }
  46. }
  47. // Get controlling terminal and redirect stdio
  48. static void open_tty(void)
  49. {
  50. if (strcmp(TT.tty_name, "-")) {
  51. if (*(TT.tty_name) != '/') TT.tty_name = xmprintf("/dev/%s", TT.tty_name);
  52. // Sends SIGHUP to all foreground process if Session leader don't die,Ignore
  53. void* handler = signal(SIGHUP, SIG_IGN);
  54. ioctl(0, TIOCNOTTY, 0); // Giveup if there is any controlling terminal
  55. signal(SIGHUP, handler);
  56. if ((setsid() < 0) && (getpid() != getsid(0))) perror_exit("setsid");
  57. xclose(0);
  58. xopen_stdio(TT.tty_name, O_RDWR|O_NDELAY|O_CLOEXEC);
  59. fcntl(0, F_SETFL, fcntl(0, F_GETFL) & ~O_NONBLOCK); // Block read
  60. dup2(0, 1);
  61. dup2(0, 2);
  62. if (ioctl(0, TIOCSCTTY, 1) < 0) perror_msg("ioctl(TIOCSCTTY)");
  63. if (!isatty(0)) perror_exit("/dev/%s: not a tty", TT.tty_name);
  64. chown(TT.tty_name, 0, 0); // change ownership, Hope login will change this
  65. chmod(TT.tty_name, 0620);
  66. } else { // We already have opened TTY
  67. if (setsid() < 0) perror_msg("setsid failed");
  68. if ((fcntl(0, F_GETFL) & (O_RDWR|O_RDONLY|O_WRONLY)) != O_RDWR)
  69. perror_exit("no read/write permission");
  70. }
  71. }
  72. static void termios_init(void)
  73. {
  74. if (tcgetattr(0, &TT.termios) < 0) perror_exit("tcgetattr");
  75. // Flush input and output queues, important for modems!
  76. tcflush(0, TCIOFLUSH);
  77. TT.termios.c_cflag &= (0|CSTOPB|PARENB|PARODD);
  78. #ifdef CRTSCTS
  79. if (FLAG(h)) TT.termios.c_cflag |= CRTSCTS;
  80. #endif
  81. if (FLAG(L)) TT.termios.c_cflag |= CLOCAL;
  82. TT.termios.c_cc[VTIME] = 0;
  83. TT.termios.c_cc[VMIN] = 1;
  84. TT.termios.c_oflag = OPOST|ONLCR;
  85. TT.termios.c_cflag |= CS8|CREAD|HUPCL|CBAUDEX;
  86. // login will disable echo for passwd.
  87. TT.termios.c_lflag |= ISIG|ICANON|ECHO|ECHOE|ECHOK|ECHOKE;
  88. TT.termios.c_cc[VINTR] = CTL('C');
  89. TT.termios.c_cc[VQUIT] = CTL('\\');
  90. TT.termios.c_cc[VEOF] = CTL('D');
  91. TT.termios.c_cc[VEOL] = '\n';
  92. TT.termios.c_cc[VKILL] = CTL('U');
  93. TT.termios.c_cc[VERASE] = 127; // CERASE
  94. TT.termios.c_iflag = ICRNL|IXON|IXOFF;
  95. // Set non-zero baud rate. Zero baud rate left it unchanged.
  96. if (TT.speeds[0] != 0) xsetspeed(&TT.termios, TT.speeds[0]);
  97. if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
  98. }
  99. // Get the baud rate from modems CONNECT mesage, Its of form <junk><BAUD><Junk>
  100. static void sense_baud(void)
  101. {
  102. int vmin, speed;
  103. ssize_t size;
  104. char *ptr;
  105. vmin = TT.termios.c_cc[VMIN]; // Store old
  106. TT.termios.c_cc[VMIN] = 0; // No block even queue is empty.
  107. if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
  108. size = readall(0, TT.buff, sizeof(TT.buff)-1);
  109. if (size > 0) {
  110. for (ptr = TT.buff; ptr < TT.buff+size; ptr++) {
  111. if (isdigit(*ptr)) {
  112. speed = atolx_range(ptr, 0, INT_MAX);
  113. if (speed > 0) xsetspeed(&TT.termios, speed);
  114. break;
  115. }
  116. }
  117. }
  118. TT.termios.c_cc[VMIN] = vmin; //restore old value
  119. if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
  120. }
  121. // Print /etc/isuue with taking care of each escape sequence
  122. void write_issue(char *file, struct utsname *uts)
  123. {
  124. char buff[20] = {0,};
  125. int fd = open(TT.f, O_RDONLY), size;
  126. if (fd < 0) return;
  127. while ((size = readall(fd, buff, 1)) > 0) {
  128. char *ch = buff;
  129. if (*ch == '\\' || *ch == '%') {
  130. if (readall(fd, buff, 1) <= 0) perror_exit("readall");
  131. if (*ch == 's') fputs(uts->sysname, stdout);
  132. if (*ch == 'n'|| *ch == 'h') fputs(uts->nodename, stdout);
  133. if (*ch == 'r') fputs(uts->release, stdout);
  134. if (*ch == 'm') fputs(uts->machine, stdout);
  135. if (*ch == 'l') fputs(TT.tty_name, stdout);
  136. } else xputc(*ch);
  137. }
  138. }
  139. // Read login name and print prompt and Issue file.
  140. static int read_login_name(void)
  141. {
  142. tcflush(0, TCIFLUSH); // Flush pending speed switches
  143. while (1) {
  144. struct utsname uts;
  145. int i = 0;
  146. uname(&uts);
  147. if (!FLAG(i)) write_issue(TT.f, &uts);
  148. printf("%s login: ", uts.nodename);
  149. xflush(1);
  150. TT.buff[0] = getchar();
  151. if (!TT.buff[0] && TT.sc > 1) return 0; // Switch speed
  152. if (TT.buff[0] == '\n') continue;
  153. if (TT.buff[0] != '\n')
  154. if (!fgets(&TT.buff[1], HOSTNAME_SIZE-1, stdin)) _exit(1);
  155. while (i < HOSTNAME_SIZE-1 && isgraph(TT.buff[i])) i++;
  156. TT.buff[i] = 0;
  157. break;
  158. }
  159. return 1;
  160. }
  161. static void utmp_entry(void)
  162. {
  163. struct utmpx entry = {.ut_pid = getpid()}, *ep;
  164. int fd;
  165. // We're responsible for ensuring that the utmp file exists.
  166. if (access(_PATH_UTMP, F_OK) && (fd = open(_PATH_UTMP, O_CREAT, 0664)) != -1)
  167. close(fd);
  168. // Find any existing entry.
  169. setutxent();
  170. while ((ep = getutxent()))
  171. if (ep->ut_pid == entry.ut_pid && ep->ut_type >= INIT_PROCESS) break;
  172. if (ep) entry = *ep;
  173. else entry.ut_type = LOGIN_PROCESS;
  174. // Modify.
  175. entry.ut_tv.tv_sec = time(0);
  176. xstrncpy(entry.ut_user, "LOGIN", sizeof(entry.ut_user));
  177. xstrncpy(entry.ut_line, ttyname(0) + strlen("/dev/"), sizeof(entry.ut_line));
  178. if (FLAG(H)) xstrncpy(entry.ut_host, TT.H, sizeof(entry.ut_host));
  179. // Write.
  180. pututxline(&entry);
  181. endutxent();
  182. }
  183. void getty_main(void)
  184. {
  185. char ch, *cmd[3] = {TT.l ? : "/bin/login", 0, 0}; // space to add username
  186. if (!FLAG(f)) TT.f = "/etc/issue";
  187. // parse arguments and set $TERM
  188. if (isdigit(**toys.optargs)) {
  189. parse_speeds(*toys.optargs);
  190. if (*++toys.optargs) TT.tty_name = xmprintf("%s", *toys.optargs);
  191. } else {
  192. TT.tty_name = xmprintf("%s", *toys.optargs);
  193. if (*++toys.optargs) parse_speeds(*toys.optargs);
  194. }
  195. if (*++toys.optargs) setenv("TERM", *toys.optargs, 1);
  196. open_tty();
  197. termios_init();
  198. tcsetpgrp(0, getpid());
  199. utmp_entry();
  200. if (FLAG(I)) xputsn(TT.I);
  201. if (FLAG(m)) sense_baud();
  202. if (FLAG(t)) alarm(TT.t);
  203. if (FLAG(w)) while (readall(0, &ch, 1) != 1) if (ch=='\n' || ch=='\r') break;
  204. if (!FLAG(n)) {
  205. int index = 1; // 0th we already set.
  206. for (;;) {
  207. if (read_login_name()) break;
  208. index %= TT.sc;
  209. xsetspeed(&TT.termios, TT.speeds[index]);
  210. //Necessary after cfsetspeed
  211. if (tcsetattr(0, TCSANOW, &TT.termios) < 0) perror_exit("tcsetattr");
  212. }
  213. cmd[1] = TT.buff; //put the username in the login command line
  214. }
  215. xexec(cmd);
  216. }