getty.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455
  1. /*++
  2. Copyright (c) 2015 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. getty.c
  5. Abstract:
  6. This module implements the getty command, which connects to a terminal,
  7. gets the user name, and runs login to fire up a user session.
  8. Author:
  9. Evan Green 16-Mar-2015
  10. Environment:
  11. POSIX
  12. --*/
  13. //
  14. // ------------------------------------------------------------------- Includes
  15. //
  16. #include <minoca/lib/types.h>
  17. #include <assert.h>
  18. #include <ctype.h>
  19. #include <errno.h>
  20. #include <fcntl.h>
  21. #include <getopt.h>
  22. #include <signal.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <sys/ioctl.h>
  26. #include <syslog.h>
  27. #include <termios.h>
  28. #include <unistd.h>
  29. #include <utmpx.h>
  30. #include "../swlib.h"
  31. #include "lutil.h"
  32. //
  33. // --------------------------------------------------------------------- Macros
  34. //
  35. //
  36. // This macro converts the given character into its control code.
  37. //
  38. #define GETTY_CONTROL(_Character) ((_Character) ^ 0x40)
  39. //
  40. // ---------------------------------------------------------------- Definitions
  41. //
  42. #define GETTY_VERSION_MAJOR 1
  43. #define GETTY_VERSION_MINOR 0
  44. #define GETTY_USAGE \
  45. "usage: getty [options] port baud,... [term]\n" \
  46. "The getty utility opens a terminal, prompts for a login name, and \n" \
  47. "executes login to create a new user session. Port is a device (off of \n" \
  48. "/dev if the path is relative). Options are:\n" \
  49. " -8, --8bits -- Assume the terminal is 8-bit clean, disable parity \n" \
  50. " detection.\n" \
  51. " -a, --autologin=user -- Log the given user in automatically without \n" \
  52. " asking for a username or password.\n" \
  53. " -f, --issue-file=file -- Set the given issue file instead of " \
  54. "/etc/issue\n" \
  55. " -H, --host=host -- Set the given host into utmp.\n" \
  56. " -I, --init-string=string -- Send the given init string before \n" \
  57. " anything else. Non-printable characters can be escaped \n" \
  58. " (eg. \\012 is ASCII 10).\n" \
  59. " -l, --login-program=program -- Set the given login program instead \n" \
  60. " of /bin/login.\n" \
  61. " -L, --local-line -- The line is a local line without the need for \n" \
  62. " carrier detect.\n" \
  63. " -m, --extract-baud -- Try to detect the baud rate based on the \n" \
  64. " HAYES-compatible CONNECT string.\n" \
  65. " -n, --skip-login -- Don't prompt for a login name.\n" \
  66. " -t, --timeout=timeout -- Terminate if no user name could be read in \n" \
  67. " the given number of seconds.\n" \
  68. " -w, --wait-cr -- Wait for the terminal to send a carraige-return or \n" \
  69. " line feed character before sending the issue file and login \n" \
  70. " prompt.\n" \
  71. " --noclear -- Don't clear the screen.\n" \
  72. " --help -- Displays this help text and exits.\n" \
  73. " --version -- Displays the application version and exits.\n"
  74. #define GETTY_OPTIONS_STRING "8a:f:H:I:l:Lmnt:wHV"
  75. #define GETTY_LOGIN_PATH "/bin/login"
  76. //
  77. // The clear screen sequence resets the scroll region, returns the cursor to
  78. // the home position, and clears the screen below the cursor.
  79. //
  80. #define GETTY_CLEAR_SEQUENCE "\033[r\033[H\033[J"
  81. #define GETTY_CLEAR_SEQUENCE_SIZE 9
  82. //
  83. // Define the maximum number of alternate baud rates.
  84. //
  85. #define GETTY_MAX_RATES 10
  86. //
  87. // Define application options.
  88. //
  89. //
  90. // Set this option for a local terminal that doesn't need carrier detect.
  91. //
  92. #define GETTY_OPTION_LOCAL 0x00000001
  93. //
  94. // Set this option to autodetect the baud rate.
  95. //
  96. #define GETTY_OPTION_AUTO_BAUD 0x00000002
  97. //
  98. // Set this option to skip prompting for a login name.
  99. //
  100. #define GETTY_OPTION_NO_LOGIN_NAME 0x00000004
  101. //
  102. // Set this option to wait for a carraige return before spitting out the issue
  103. // file and prompt.
  104. //
  105. #define GETTY_OPTION_WAIT_CR 0x00000008
  106. //
  107. // Set this option to automatically log in the given user.
  108. //
  109. #define GETTY_OPTION_AUTO_LOGIN 0x00000010
  110. //
  111. // Set this option to not clear the screen.
  112. //
  113. #define GETTY_OPTION_NO_CLEAR 0x00000020
  114. //
  115. // ------------------------------------------------------ Data Type Definitions
  116. //
  117. /*++
  118. Structure Description:
  119. This structure stores the tuple of a baud rate and its corresponding value.
  120. Members:
  121. Rate - Stores the baud rate.
  122. Value - Stores the Bxxx value (like B9600).
  123. --*/
  124. typedef struct _GETTY_RATE {
  125. INT Rate;
  126. INT Value;
  127. } GETTY_RATE, *PGETTY_RATE;
  128. //
  129. // ----------------------------------------------- Internal Function Prototypes
  130. //
  131. PSTR
  132. GettyParseInitString (
  133. PSTR String,
  134. size_t *ResultSize
  135. );
  136. PSTR
  137. GettyOpenTerminal (
  138. PSTR TtyName
  139. );
  140. INT
  141. GettySetTerminalAttributes (
  142. ULONG Options,
  143. struct termios *Settings,
  144. INT BaudValue
  145. );
  146. INT
  147. GettyFinalizeTerminal (
  148. struct termios *Settings
  149. );
  150. PSTR
  151. GettyGetUserName (
  152. ULONG Options,
  153. PSTR IssueFile,
  154. struct termios *Settings,
  155. UINTN BaudRateCount,
  156. PSTR TerminalName
  157. );
  158. INT
  159. GettyConvertBaudRateToValue (
  160. INT BaudRate
  161. );
  162. INT
  163. GettyWriteBuffer (
  164. int Descriptor,
  165. PVOID Buffer,
  166. size_t Size
  167. );
  168. INT
  169. GettyDetectBaudRate (
  170. struct termios *Settings
  171. );
  172. void
  173. GettyAlarmSignalHandler (
  174. int Signal
  175. );
  176. //
  177. // -------------------------------------------------------------------- Globals
  178. //
  179. struct option GettyLongOptions[] = {
  180. {"8bits", no_argument, 0, '8'},
  181. {"autologin", required_argument, 0, 'a'},
  182. {"issue-file", required_argument, 0, 'f'},
  183. {"host", required_argument, 0, 'H'},
  184. {"init-string", required_argument, 0, 'I'},
  185. {"login-program", required_argument, 0, 'l'},
  186. {"local-line", no_argument, 0, 'L'},
  187. {"extract-baud", no_argument, 0, 'm'},
  188. {"skip-login", no_argument, 0, 'n'},
  189. {"timeout", required_argument, 0, 't'},
  190. {"wait-cr", no_argument, 0, 'w'},
  191. {"noclear", no_argument, 0, 'N'},
  192. {"help", no_argument, 0, 'h'},
  193. {"version", no_argument, 0, 'V'},
  194. {NULL, 0, 0, 0},
  195. };
  196. GETTY_RATE GettyRates[] = {
  197. {50, B50},
  198. {75, B75},
  199. {110, B110},
  200. {134, B134},
  201. {150, B150},
  202. {200, B200},
  203. {300, B300},
  204. {600, B600},
  205. {1200, B1200},
  206. {1800, B1800},
  207. {2400, B2400},
  208. {4800, B4800},
  209. {9600, B9600},
  210. {19200, B19200},
  211. {38400, B38400},
  212. {57600, B57600},
  213. {115200, B115200},
  214. {230400, B230400},
  215. {0, 0}
  216. };
  217. BOOL GettyAlarmFired = FALSE;
  218. //
  219. // ------------------------------------------------------------------ Functions
  220. //
  221. INT
  222. GettyMain (
  223. INT ArgumentCount,
  224. CHAR **Arguments
  225. )
  226. /*++
  227. Routine Description:
  228. This routine is the main entry point for the getty utility.
  229. Arguments:
  230. ArgumentCount - Supplies the number of command line arguments the program
  231. was invoked with.
  232. Arguments - Supplies a tokenized array of command line arguments.
  233. Return Value:
  234. Returns an integer exit code. 0 for success, nonzero otherwise.
  235. --*/
  236. {
  237. PSTR AfterScan;
  238. BOOL AlarmSet;
  239. ULONG ArgumentIndex;
  240. UINTN BaudIndex;
  241. INT BaudRate;
  242. UINTN BaudRateCount;
  243. INT BaudRates[GETTY_MAX_RATES];
  244. PSTR BaudString;
  245. ssize_t BytesRead;
  246. CHAR Character;
  247. PSTR Copy;
  248. PSTR CurrentBaudString;
  249. INT FileDescriptor;
  250. int Flags;
  251. PSTR Host;
  252. PSTR InitString;
  253. size_t InitStringSize;
  254. PSTR IssuePath;
  255. UINTN LoginArgumentCount;
  256. PSTR LoginArguments[5];
  257. PSTR LoginProgram;
  258. PSTR NextComma;
  259. int Null;
  260. INT Option;
  261. ULONG Options;
  262. void *OriginalHandler;
  263. pid_t ProcessId;
  264. int Status;
  265. PSTR Swap;
  266. pid_t TerminalSession;
  267. struct termios TerminalSettings;
  268. PSTR TermVariable;
  269. INTN Timeout;
  270. PSTR TtyName;
  271. PSTR TtyPath;
  272. PSTR UserName;
  273. AlarmSet = FALSE;
  274. BaudRateCount = 0;
  275. BaudString = NULL;
  276. Copy = NULL;
  277. Host = NULL;
  278. InitString = NULL;
  279. InitStringSize = 0;
  280. IssuePath = ISSUE_PATH;
  281. LoginProgram = GETTY_LOGIN_PATH;
  282. Options = 0;
  283. OriginalHandler = NULL;
  284. TermVariable = NULL;
  285. Timeout = -1;
  286. TtyName = NULL;
  287. TtyPath = NULL;
  288. UserName = NULL;
  289. //
  290. // Process the control arguments.
  291. //
  292. while (TRUE) {
  293. Option = getopt_long(ArgumentCount,
  294. Arguments,
  295. GETTY_OPTIONS_STRING,
  296. GettyLongOptions,
  297. NULL);
  298. if (Option == -1) {
  299. break;
  300. }
  301. if ((Option == '?') || (Option == ':')) {
  302. Status = 1;
  303. goto MainEnd;
  304. }
  305. switch (Option) {
  306. case '8':
  307. break;
  308. case 'a':
  309. Options |= GETTY_OPTION_AUTO_LOGIN | GETTY_OPTION_NO_LOGIN_NAME;
  310. UserName = optarg;
  311. break;
  312. case 'f':
  313. IssuePath = optarg;
  314. break;
  315. case 'H':
  316. Host = optarg;
  317. break;
  318. case 'I':
  319. InitString = GettyParseInitString(optarg, &InitStringSize);
  320. if (InitString == NULL) {
  321. SwPrintError(0, InitString, "Invalid init string");
  322. Status = 1;
  323. goto MainEnd;
  324. }
  325. break;
  326. case 'l':
  327. LoginProgram = optarg;
  328. break;
  329. case 'L':
  330. Options |= GETTY_OPTION_LOCAL;
  331. break;
  332. case 'm':
  333. Options |= GETTY_OPTION_AUTO_BAUD;
  334. break;
  335. case 'n':
  336. Options |= GETTY_OPTION_NO_LOGIN_NAME;
  337. break;
  338. case 'N':
  339. Options |= GETTY_OPTION_NO_CLEAR;
  340. break;
  341. case 't':
  342. Timeout = strtoul(optarg, &AfterScan, 10);
  343. if (AfterScan == optarg) {
  344. SwPrintError(0, optarg, "Invalid timeout");
  345. Status = 1;
  346. goto MainEnd;
  347. }
  348. break;
  349. case 'w':
  350. Options |= GETTY_OPTION_WAIT_CR;
  351. break;
  352. case 'V':
  353. SwPrintVersion(GETTY_VERSION_MAJOR, GETTY_VERSION_MINOR);
  354. return 1;
  355. case 'h':
  356. printf(GETTY_USAGE);
  357. return 1;
  358. default:
  359. assert(FALSE);
  360. Status = 1;
  361. goto MainEnd;
  362. }
  363. }
  364. ArgumentIndex = optind;
  365. if (ArgumentIndex > ArgumentCount) {
  366. ArgumentIndex = ArgumentCount;
  367. }
  368. if (ArgumentIndex + 1 >= ArgumentCount) {
  369. SwPrintError(0, NULL, "Argument expected");
  370. Status = 1;
  371. goto MainEnd;
  372. }
  373. TtyPath = Arguments[ArgumentIndex];
  374. BaudString = Arguments[ArgumentIndex + 1];
  375. ArgumentIndex += 2;
  376. //
  377. // Allow for one more argument, the TERM variable.
  378. //
  379. if (ArgumentIndex < ArgumentCount) {
  380. TermVariable = Arguments[ArgumentIndex];
  381. ArgumentIndex += 1;
  382. setenv("TERM", TermVariable, 1);
  383. }
  384. if (ArgumentIndex != ArgumentCount) {
  385. SwPrintError(0, Arguments[ArgumentIndex], "Unexpected argument");
  386. Status = 1;
  387. goto MainEnd;
  388. }
  389. //
  390. // Allow both "tty baud" and "baud tty".
  391. //
  392. if (isdigit(*TtyPath) != 0) {
  393. Swap = TtyPath;
  394. TtyPath = BaudString;
  395. BaudString = Swap;
  396. }
  397. //
  398. // Parse the baud rates string.
  399. //
  400. Copy = strdup(BaudString);
  401. if (Copy == NULL) {
  402. Status = ENOMEM;
  403. goto MainEnd;
  404. }
  405. CurrentBaudString = Copy;
  406. while ((CurrentBaudString != NULL) && (BaudRateCount < GETTY_MAX_RATES)) {
  407. NextComma = strchr(CurrentBaudString, ',');
  408. if (NextComma != NULL) {
  409. *NextComma = '\0';
  410. NextComma += 1;
  411. }
  412. BaudRate = strtoul(CurrentBaudString, &AfterScan, 10);
  413. if (CurrentBaudString == AfterScan) {
  414. SwPrintError(0, CurrentBaudString, "Invalid baud rate");
  415. Status = 1;
  416. goto MainEnd;
  417. }
  418. BaudRates[BaudRateCount] = GettyConvertBaudRateToValue(BaudRate);
  419. if (BaudRates[BaudRateCount] == -1) {
  420. SwPrintError(0, CurrentBaudString, "Unsupported baud rate");
  421. } else {
  422. BaudRateCount += 1;
  423. }
  424. CurrentBaudString = NextComma;
  425. }
  426. free(Copy);
  427. Copy = NULL;
  428. if (BaudRateCount == 0) {
  429. SwPrintError(0, NULL, "No baud rates specified");
  430. Status = 1;
  431. goto MainEnd;
  432. }
  433. //
  434. // Create a new session and process group. Failure to create a new session
  435. // may occur if the process is already a session leader.
  436. //
  437. ProcessId = setsid();
  438. if (ProcessId < 0) {
  439. ProcessId = getpid();
  440. if (getsid(0) != ProcessId) {
  441. SwPrintError(0, NULL, "Failed to create new session");
  442. Status = 1;
  443. goto MainEnd;
  444. }
  445. TtyName = ttyname(STDIN_FILENO);
  446. if (TtyName != NULL) {
  447. FileDescriptor = open(TtyName, O_RDWR | O_NONBLOCK);
  448. if (FileDescriptor >= 0) {
  449. OriginalHandler = signal(SIGHUP, SIG_IGN);
  450. ioctl(FileDescriptor, TIOCNOTTY);
  451. close(FileDescriptor);
  452. signal(SIGHUP, OriginalHandler);
  453. }
  454. }
  455. TtyName = NULL;
  456. }
  457. //
  458. // Close all other descriptors, open the log, and open the terminal.
  459. //
  460. SwCloseFrom(STDERR_FILENO + 1);
  461. openlog("getty", LOG_PID, LOG_AUTH);
  462. Null = open("/dev/null", O_RDWR);
  463. if (Null < 0) {
  464. close(STDOUT_FILENO);
  465. close(STDERR_FILENO);
  466. } else {
  467. dup2(Null, STDOUT_FILENO);
  468. dup2(Null, STDERR_FILENO);
  469. close(Null);
  470. }
  471. TtyName = GettyOpenTerminal(TtyPath);
  472. if (TtyName == NULL) {
  473. Status = 1;
  474. goto MainEnd;
  475. }
  476. //
  477. // Clear non-blocking mode.
  478. //
  479. Flags = fcntl(STDIN_FILENO, F_GETFL);
  480. Flags &= ~O_NONBLOCK;
  481. fcntl(STDIN_FILENO, F_SETFL, Flags);
  482. dup2(STDIN_FILENO, STDOUT_FILENO);
  483. dup2(STDIN_FILENO, STDERR_FILENO);
  484. //
  485. // Set the controlling terminal.
  486. //
  487. TerminalSession = tcgetsid(STDIN_FILENO);
  488. if ((TerminalSession < 0) || (TerminalSession != ProcessId)) {
  489. if (ioctl(STDIN_FILENO, TIOCSCTTY, 1) < 0) {
  490. SwPrintError(errno, TtyName, "Failed to set controlling terminal");
  491. Status = 1;
  492. goto MainEnd;
  493. }
  494. }
  495. if (tcsetpgrp(STDIN_FILENO, ProcessId) < 0) {
  496. SwPrintError(errno, TtyName, "Failed to set process group");
  497. Status = 1;
  498. goto MainEnd;
  499. }
  500. if (tcgetattr(STDIN_FILENO, &TerminalSettings) < 0) {
  501. SwPrintError(errno, TtyName, "Failed to get terminal settings");
  502. Status = 1;
  503. goto MainEnd;
  504. }
  505. SwUpdateUtmp(ProcessId, LOGIN_PROCESS, TtyName, "LOGIN", Host);
  506. //
  507. // Set up the terminal attributes.
  508. //
  509. Status = GettySetTerminalAttributes(Options,
  510. &TerminalSettings,
  511. BaudRates[0]);
  512. if (Status != 0) {
  513. SwPrintError(errno, TtyName, "Failed to set terminal attributes");
  514. goto MainEnd;
  515. }
  516. //
  517. // Write the init string if one was supplied.
  518. //
  519. if (InitString != NULL) {
  520. Status = GettyWriteBuffer(STDOUT_FILENO, InitString, InitStringSize);
  521. if (Status != 0) {
  522. SwPrintError(Status, NULL, "Warning: Failed to write init string");
  523. }
  524. }
  525. //
  526. // Auto-detect the baud rate if requested.
  527. //
  528. if ((Options & GETTY_OPTION_AUTO_BAUD) != 0) {
  529. Status = GettyDetectBaudRate(&TerminalSettings);
  530. if (Status != 0) {
  531. SwPrintError(Status, NULL, "Warning: Failed to detect baud rate");
  532. }
  533. }
  534. //
  535. // Clear the screen.
  536. //
  537. if ((Options & GETTY_OPTION_NO_CLEAR) == 0) {
  538. GettyWriteBuffer(STDOUT_FILENO,
  539. GETTY_CLEAR_SEQUENCE,
  540. GETTY_CLEAR_SEQUENCE_SIZE);
  541. }
  542. //
  543. // Set the timeout.
  544. //
  545. if (Timeout != -1) {
  546. OriginalHandler = signal(SIGALRM, GettyAlarmSignalHandler);
  547. alarm(Timeout);
  548. AlarmSet = TRUE;
  549. }
  550. //
  551. // Wait for a CR or LF if requested.
  552. //
  553. if ((Options & GETTY_OPTION_WAIT_CR) != 0) {
  554. while (TRUE) {
  555. BytesRead = read(STDIN_FILENO, &Character, 1);
  556. if ((BytesRead < 0) && (errno == EINTR)) {
  557. if (GettyAlarmFired != FALSE) {
  558. SwPrintError(0,
  559. NULL,
  560. "Giving up due to %d second timeout",
  561. Timeout);
  562. GettyFinalizeTerminal(&TerminalSettings);
  563. Status = ETIMEDOUT;
  564. goto MainEnd;
  565. }
  566. }
  567. if (BytesRead <= 0) {
  568. break;
  569. }
  570. if ((Character == '\r') || (Character == '\n')) {
  571. break;
  572. }
  573. }
  574. }
  575. //
  576. // Read the user name.
  577. //
  578. if ((Options & GETTY_OPTION_NO_LOGIN_NAME) == 0) {
  579. BaudIndex = 0;
  580. while (TRUE) {
  581. UserName = GettyGetUserName(Options,
  582. IssuePath,
  583. &TerminalSettings,
  584. BaudRateCount,
  585. TtyName);
  586. if (UserName != NULL) {
  587. break;
  588. }
  589. if (GettyAlarmFired != FALSE) {
  590. SwPrintError(0,
  591. NULL,
  592. "Giving up due to %d second timeout",
  593. Timeout);
  594. Status = ETIMEDOUT;
  595. goto MainEnd;
  596. }
  597. BaudIndex = (BaudIndex + 1) % BaudRateCount;
  598. cfsetispeed(&TerminalSettings, BaudRates[BaudIndex]);
  599. cfsetospeed(&TerminalSettings, BaudRates[BaudIndex]);
  600. Status = tcsetattr(STDIN_FILENO, TCSANOW, &TerminalSettings);
  601. if (Status != 0) {
  602. Status = errno;
  603. SwPrintError(Status,
  604. TtyName,
  605. "Failed to set terminal settings");
  606. goto MainEnd;
  607. }
  608. }
  609. } else {
  610. //
  611. // Guess that the terminal hands back carraige returns.
  612. //
  613. TerminalSettings.c_iflag |= ICRNL;
  614. if ((Options & GETTY_OPTION_AUTO_LOGIN) != 0) {
  615. if (IssuePath != NULL) {
  616. SwPrintLoginIssue(IssuePath, TtyName);
  617. }
  618. SwPrintLoginPrompt();
  619. printf("%s (automatic login)", UserName);
  620. fflush(NULL);
  621. }
  622. }
  623. if (Timeout != -1) {
  624. alarm(0);
  625. signal(SIGALRM, OriginalHandler);
  626. AlarmSet = FALSE;
  627. }
  628. GettyFinalizeTerminal(&TerminalSettings);
  629. //
  630. // Fire off the login program.
  631. //
  632. LoginArguments[0] = LoginProgram;
  633. LoginArgumentCount = 1;
  634. if ((Options & GETTY_OPTION_AUTO_LOGIN) != 0) {
  635. LoginArguments[LoginArgumentCount] = "-f";
  636. LoginArgumentCount += 1;
  637. }
  638. LoginArguments[LoginArgumentCount] = "--";
  639. LoginArgumentCount += 1;
  640. if (UserName != NULL) {
  641. LoginArguments[LoginArgumentCount] = UserName;
  642. LoginArgumentCount += 1;
  643. }
  644. LoginArguments[LoginArgumentCount] = NULL;
  645. LoginArgumentCount += 1;
  646. execvp(LoginArguments[0], LoginArguments);
  647. SwPrintError(errno, LoginArguments[0], "Could not exec");
  648. Status = 1;
  649. MainEnd:
  650. if (AlarmSet != FALSE) {
  651. alarm(0);
  652. signal(SIGALRM, OriginalHandler);
  653. }
  654. if ((UserName != NULL) && ((Options & GETTY_OPTION_AUTO_LOGIN) == 0)) {
  655. free(UserName);
  656. }
  657. if (TtyName != NULL) {
  658. free(TtyName);
  659. }
  660. if (Copy != NULL) {
  661. free(Copy);
  662. }
  663. return Status;
  664. }
  665. //
  666. // --------------------------------------------------------- Internal Functions
  667. //
  668. PSTR
  669. GettyParseInitString (
  670. PSTR String,
  671. size_t *ResultSize
  672. )
  673. /*++
  674. Routine Description:
  675. This routine parses the init string, handling escape characters.
  676. Arguments:
  677. String - Supplies a pointer to the input string to unescape.
  678. ResultSize - Supplies a pointer where the size of the output string will be
  679. returned on success.
  680. Return Value:
  681. Returns a pointer to a newly allocated string on success. The caller is
  682. responsible for freeing this buffer when finished.
  683. NULL on allocation failure.
  684. --*/
  685. {
  686. CHAR Character;
  687. UINTN Index;
  688. PSTR Out;
  689. PSTR Result;
  690. size_t Size;
  691. Result = malloc(strlen(String));
  692. if (Result == NULL) {
  693. return NULL;
  694. }
  695. Out = Result;
  696. Size = 0;
  697. while (*String != '\0') {
  698. if (*String == '\\') {
  699. String += 1;
  700. if (*String == '\\') {
  701. Character = '\\';
  702. String += 1;
  703. } else {
  704. Character = 0;
  705. for (Index = 0; Index < 3; Index += 1) {
  706. if ((*String >= '0') && (*String <= '7')) {
  707. Character *= 8;
  708. Character += *String - '0';
  709. String += 1;
  710. } else {
  711. break;
  712. }
  713. }
  714. }
  715. *Out = Character;
  716. //
  717. // This is a non-escaped normal character.
  718. //
  719. } else {
  720. *Out = *String;
  721. String += 1;
  722. }
  723. Out += 1;
  724. Size += 1;
  725. }
  726. *ResultSize = Size;
  727. return Result;
  728. }
  729. PSTR
  730. GettyOpenTerminal (
  731. PSTR TtyName
  732. )
  733. /*++
  734. Routine Description:
  735. This routine opens up the specified terminal.
  736. Arguments:
  737. TtyName - Supplies the terminal name.
  738. Return Value:
  739. Returns the final allocated terminal name on success. The caller is
  740. responsible for freeing this buffer.
  741. NULL on failure.
  742. --*/
  743. {
  744. int Descriptor;
  745. PSTR FinalName;
  746. int Result;
  747. size_t Size;
  748. if (strcmp(TtyName, "-") == 0) {
  749. Result = fcntl(STDIN_FILENO, F_GETFL);
  750. if ((Result & (O_RDWR | O_RDONLY | O_WRONLY)) != O_RDWR) {
  751. SwPrintError(0, NULL, "stdin not open for read and write");
  752. return NULL;
  753. }
  754. FinalName = ttyname(STDIN_FILENO);
  755. if (FinalName == NULL) {
  756. return NULL;
  757. }
  758. return strdup(FinalName);
  759. } else {
  760. if (*TtyName == '/') {
  761. FinalName = strdup(TtyName);
  762. if (FinalName == NULL) {
  763. return NULL;
  764. }
  765. } else {
  766. Size = 5 + strlen(TtyName);
  767. FinalName = malloc(Size);
  768. if (FinalName == NULL) {
  769. return NULL;
  770. }
  771. snprintf(FinalName, Size, "/dev/%s", TtyName);
  772. }
  773. close(STDIN_FILENO);
  774. Descriptor = open(FinalName, O_RDWR | O_NONBLOCK);
  775. if (Descriptor < 0) {
  776. SwPrintError(errno, FinalName, "Failed to open");
  777. free(FinalName);
  778. return NULL;
  779. }
  780. dup2(Descriptor, STDIN_FILENO);
  781. Result = fchown(STDIN_FILENO, 0, 0);
  782. if (Result != 0) {
  783. free(FinalName);
  784. return NULL;
  785. }
  786. fchmod(STDIN_FILENO, S_IRUSR | S_IWUSR | S_IWGRP);
  787. if (Descriptor != STDIN_FILENO) {
  788. close(Descriptor);
  789. }
  790. }
  791. return FinalName;
  792. }
  793. INT
  794. GettySetTerminalAttributes (
  795. ULONG Options,
  796. struct termios *Settings,
  797. INT BaudValue
  798. )
  799. /*++
  800. Routine Description:
  801. This routine sets the terminal settings according to the given attributes.
  802. Arguments:
  803. Options - Supplies the application options. See GETTY_OPTION_* definitions.
  804. Settings - Supplies a pointer to the settings.
  805. BaudValue - Supplies the baud rate (definition) to set.
  806. Return Value:
  807. 0 on success.
  808. Non-zero on failure.
  809. --*/
  810. {
  811. tcdrain(STDIN_FILENO);
  812. tcflush(STDIN_FILENO, TCIOFLUSH);
  813. if (BaudValue != B0) {
  814. cfsetispeed(Settings, BaudValue);
  815. cfsetospeed(Settings, BaudValue);
  816. }
  817. //
  818. // Set up the terminal for 8-bit raw mode with blocking I/O.
  819. //
  820. Settings->c_cflag &= (CSTOPB | PARENB | PARODD);
  821. Settings->c_cflag |= CS8 | HUPCL | CREAD;
  822. if ((Options & GETTY_OPTION_LOCAL) != 0) {
  823. Settings->c_cflag |= CLOCAL;
  824. }
  825. Settings->c_iflag = 0;
  826. Settings->c_lflag = 0;
  827. Settings->c_oflag = OPOST | ONLCR;
  828. //
  829. // Reads should release as soon as one character is available, and wait
  830. // indefinitely for that character to arrive.
  831. //
  832. Settings->c_cc[VMIN] = 1;
  833. Settings->c_cc[VTIME] = 0;
  834. return tcsetattr(STDIN_FILENO, TCSANOW, Settings);
  835. }
  836. INT
  837. GettyFinalizeTerminal (
  838. struct termios *Settings
  839. )
  840. /*++
  841. Routine Description:
  842. This routine sets the final terminal settings before launching login or
  843. exiting.
  844. Arguments:
  845. Settings - Supplies the terminal settings to set.
  846. Return Value:
  847. 0 on success.
  848. Non-zero on failure.
  849. --*/
  850. {
  851. INT Status;
  852. //
  853. // Enable software flow control.
  854. //
  855. Settings->c_iflag |= IXON | IXOFF | IMAXBEL;
  856. //
  857. // Set up canonical mode, echo, and signals.
  858. //
  859. Settings->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOKE | ECHOCTL;
  860. Settings->c_cc[VINTR] = GETTY_CONTROL('C');
  861. Settings->c_cc[VQUIT] = GETTY_CONTROL('\\');
  862. Settings->c_cc[VEOF] = GETTY_CONTROL('D');
  863. Settings->c_cc[VEOL] = '\n';
  864. Settings->c_cc[VKILL] = GETTY_CONTROL('U');
  865. Status = tcsetattr(STDIN_FILENO, TCSANOW, Settings);
  866. GettyWriteBuffer(STDOUT_FILENO, "\n", 1);
  867. if (Status == 0) {
  868. return 0;
  869. }
  870. return errno;
  871. }
  872. PSTR
  873. GettyGetUserName (
  874. ULONG Options,
  875. PSTR IssueFile,
  876. struct termios *Settings,
  877. UINTN BaudRateCount,
  878. PSTR TerminalName
  879. )
  880. /*++
  881. Routine Description:
  882. This routine reads the user name from the terminal.
  883. Arguments:
  884. Options - Supplies the application options. See GETTY_OPTION_* definitions.
  885. IssueFile - Supplies an optional pointer to the issue file to print.
  886. Settings - Supplies a pointer to the terminal settings.
  887. BaudRateCount - Supplies the number of possible baud rates.
  888. TerminalName - Supplies the name of this terminal.
  889. Return Value:
  890. 0 on success.
  891. Non-zero on failure.
  892. --*/
  893. {
  894. CHAR Character;
  895. PSTR Current;
  896. BOOL Done;
  897. CHAR Line[256];
  898. usleep(100000);
  899. tcflush(STDIN_FILENO, TCIFLUSH);
  900. Line[0] = '\0';
  901. do {
  902. if (IssueFile != NULL) {
  903. SwPrintLoginIssue(IssueFile, TerminalName);
  904. }
  905. SwPrintLoginPrompt();
  906. Current = Line;
  907. //
  908. // Loop reading characters.
  909. //
  910. Done = FALSE;
  911. while (Done == FALSE) {
  912. errno = EINTR;
  913. if (read(STDIN_FILENO, &Character, 1) < 1) {
  914. GettyFinalizeTerminal(Settings);
  915. if ((errno == EINTR) || (errno == EIO)) {
  916. return NULL;
  917. }
  918. SwPrintError(errno, NULL, "Failed to read");
  919. return NULL;
  920. }
  921. switch (Character) {
  922. case '\r':
  923. Settings->c_iflag |= ICRNL;
  924. //
  925. // Fall through.
  926. //
  927. case '\n':
  928. *Current = '\0';
  929. Character = '\n';
  930. GettyWriteBuffer(STDOUT_FILENO, &Character, 1);
  931. Done = TRUE;
  932. break;
  933. case GETTY_CONTROL('U'):
  934. while (Current > Line) {
  935. GettyWriteBuffer(STDOUT_FILENO, "\b \b", 3);
  936. Current -= 1;
  937. }
  938. *Current = '\0';
  939. break;
  940. case '\b':
  941. case 0x7F:
  942. if (Current > Line) {
  943. GettyWriteBuffer(STDOUT_FILENO, "\b \b", 3);
  944. Current -= 1;
  945. }
  946. break;
  947. case GETTY_CONTROL('C'):
  948. case GETTY_CONTROL('D'):
  949. GettyFinalizeTerminal(Settings);
  950. return NULL;
  951. case '\0':
  952. if (BaudRateCount > 1) {
  953. return NULL;
  954. }
  955. //
  956. // Fall through.
  957. //
  958. default:
  959. if ((Character >= ' ') &&
  960. ((Current - Line) < sizeof(Line) - 1)) {
  961. *Current = Character;
  962. Current += 1;
  963. GettyWriteBuffer(STDOUT_FILENO, &Character, 1);
  964. }
  965. break;
  966. }
  967. }
  968. } while (Line[0] == '\0');
  969. return strdup(Line);
  970. }
  971. INT
  972. GettyConvertBaudRateToValue (
  973. INT BaudRate
  974. )
  975. /*++
  976. Routine Description:
  977. This routine converts a raw baud rate into its B definition.
  978. Arguments:
  979. BaudRate - Supplies the baud rate to convert.
  980. Return Value:
  981. Returns the baud value, or -1 on failure.
  982. --*/
  983. {
  984. UINTN Index;
  985. Index = 0;
  986. while (GettyRates[Index].Rate != 0) {
  987. if (GettyRates[Index].Rate == BaudRate) {
  988. return GettyRates[Index].Value;
  989. }
  990. Index += 1;
  991. }
  992. return -1;
  993. }
  994. INT
  995. GettyWriteBuffer (
  996. int Descriptor,
  997. PVOID Buffer,
  998. size_t Size
  999. )
  1000. /*++
  1001. Routine Description:
  1002. This routine writes the full buffer into the given descriptor.
  1003. Arguments:
  1004. Descriptor - Supplies the file descriptor to write to.
  1005. Buffer - Supplies a pointer to the buffer to write.
  1006. Size - Supplies the number of bytes to write.
  1007. Return Value:
  1008. 0 on success.
  1009. Returns errno on failure.
  1010. --*/
  1011. {
  1012. ssize_t BytesWritten;
  1013. while (Size > 0) {
  1014. BytesWritten = write(Descriptor, Buffer, Size);
  1015. if (BytesWritten <= 0) {
  1016. if (errno == EINTR) {
  1017. continue;
  1018. }
  1019. return errno;
  1020. }
  1021. Buffer += BytesWritten;
  1022. Size -= BytesWritten;
  1023. }
  1024. return 0;
  1025. }
  1026. INT
  1027. GettyDetectBaudRate (
  1028. struct termios *Settings
  1029. )
  1030. /*++
  1031. Routine Description:
  1032. This routine attempts to detect the baud rate from the modem status
  1033. message.
  1034. Arguments:
  1035. Settings - Supplies a pointer to the current terminal settings.
  1036. Return Value:
  1037. 0 on success.
  1038. Non-zero on failure.
  1039. --*/
  1040. {
  1041. INT BaudRate;
  1042. INT BaudValue;
  1043. ssize_t BytesRead;
  1044. PSTR Current;
  1045. CHAR Line[256];
  1046. INT Status;
  1047. cc_t Vmin;
  1048. //
  1049. // Don't block reads.
  1050. //
  1051. Vmin = Settings->c_cc[VMIN];
  1052. Settings->c_cc[VMIN] = 0;
  1053. Status = tcsetattr(STDIN_FILENO, TCSANOW, Settings);
  1054. if (Status != 0) {
  1055. Settings->c_cc[VMIN] = Vmin;
  1056. return errno;
  1057. }
  1058. //
  1059. // Wait a bit.
  1060. //
  1061. sleep(1);
  1062. //
  1063. // Try to read the status message. It's generally a set of digits amidst
  1064. // some garbage before and after.
  1065. //
  1066. do {
  1067. errno = 0;
  1068. BytesRead = read(STDIN_FILENO, Line, sizeof(Line) - 1);
  1069. } while ((BytesRead < 0) && (errno == EINTR));
  1070. if (BytesRead > 0) {
  1071. Current = Line;
  1072. Line[BytesRead] = '\0';
  1073. while (Current < Line + BytesRead) {
  1074. if (isdigit(*Current)) {
  1075. BaudRate = strtoul(Current, NULL, 10);
  1076. BaudValue = GettyConvertBaudRateToValue(BaudRate);
  1077. if (BaudValue > 0) {
  1078. cfsetispeed(Settings, BaudValue);
  1079. cfsetospeed(Settings, BaudValue);
  1080. break;
  1081. }
  1082. }
  1083. Current += 1;
  1084. }
  1085. }
  1086. Settings->c_cc[VMIN] = Vmin;
  1087. return 0;
  1088. }
  1089. void
  1090. GettyAlarmSignalHandler (
  1091. int Signal
  1092. )
  1093. /*++
  1094. Routine Description:
  1095. This routine handles the alarm expiration.
  1096. Arguments:
  1097. Signal - Supplies the signal that fired, in this case SIGALRM.
  1098. Return Value:
  1099. None.
  1100. --*/
  1101. {
  1102. GettyAlarmFired = TRUE;
  1103. return;
  1104. }