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