pty.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814
  1. /*++
  2. Copyright (c) 2015 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. pty.c
  9. Abstract:
  10. This module implements support for working with pseudo-terminals in the
  11. C library.
  12. Author:
  13. Evan Green 2-Feb-2015
  14. Environment:
  15. User Mode C Library
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. #include "libcp.h"
  21. #include <assert.h>
  22. #include <errno.h>
  23. #include <fcntl.h>
  24. #include <limits.h>
  25. #include <grp.h>
  26. #include <pty.h>
  27. #include <stdlib.h>
  28. #include <syslog.h>
  29. #include <sys/stat.h>
  30. #include <unistd.h>
  31. //
  32. // ---------------------------------------------------------------- Definitions
  33. //
  34. //
  35. // Define the TTY group name.
  36. //
  37. #define TTY_GROUP_NAME "tty"
  38. //
  39. // Define the set of permissions that are set on grantpt.
  40. //
  41. #define TTY_SLAVE_PERMISSIONS (S_IRUSR | S_IWUSR | S_IWGRP)
  42. //
  43. // Define the conventional directory and format for pseudo-terminals.
  44. //
  45. #define PTY_PREFERRED_DIRECTORY "/dev"
  46. #define PTY_PREFERRED_DIRECTORY2 "."
  47. #define PTY_FALLBACK_DIRECTORY "/tmp"
  48. #define PTY_MASTER_PATH_FORMAT "%s/pty%dm"
  49. #define PTY_SLAVE_PATH_FORMAT "%s/pty%d"
  50. //
  51. // Define the maximum size of a conventional pseudo-terminal path.
  52. //
  53. #define PTY_PATH_MAX 50
  54. //
  55. // Define the maximum number of pseudo-terminals to try, conventionally.
  56. //
  57. #define PTY_MAX 1024
  58. //
  59. // Define the initial permission of a pseudo-terminal.
  60. //
  61. #define PTY_INITIAL_PERMISSIONS \
  62. (FILE_PERMISSION_USER_READ | FILE_PERMISSION_USER_WRITE | \
  63. FILE_PERMISSION_GROUP_READ | FILE_PERMISSION_GROUP_WRITE | \
  64. FILE_PERMISSION_OTHER_READ)
  65. //
  66. // ------------------------------------------------------ Data Type Definitions
  67. //
  68. //
  69. // ----------------------------------------------- Internal Function Prototypes
  70. //
  71. //
  72. // -------------------------------------------------------------------- Globals
  73. //
  74. //
  75. // Store a pointer to the static pseudo-terminal slave name.
  76. //
  77. PSTR ClTerminalSlaveName = NULL;
  78. //
  79. // Store the TTY group.
  80. //
  81. gid_t ClTtyGroup = -1;
  82. //
  83. // ------------------------------------------------------------------ Functions
  84. //
  85. LIBC_API
  86. int
  87. openpty (
  88. int *Master,
  89. int *Slave,
  90. char *Name,
  91. const struct termios *Settings,
  92. const struct winsize *WindowSize
  93. )
  94. /*++
  95. Routine Description:
  96. This routine creates a new pseudo-terminal device.
  97. Arguments:
  98. Master - Supplies a pointer where a file descriptor to the master will be
  99. returned on success.
  100. Slave - Supplies a pointer where a file descriptor to the slave will be
  101. returned on success.
  102. Name - Supplies an optional pointer where the name of the slave terminal
  103. will be returned on success. This buffer must be PATH_MAX size in bytes
  104. if supplied.
  105. Settings - Supplies an optional pointer to the settings to apply to the
  106. new terminal.
  107. WindowSize - Supplies an optional pointer to the window size to set in the
  108. new terminal.
  109. Return Value:
  110. 0 on success.
  111. -1 on failure, and errno will be set to contain more information.
  112. --*/
  113. {
  114. int MasterFile;
  115. CHAR Path[PATH_MAX];
  116. int Result;
  117. int SlaveFile;
  118. SlaveFile = -1;
  119. MasterFile = getpt();
  120. if (MasterFile < 0) {
  121. return -1;
  122. }
  123. Result = grantpt(MasterFile);
  124. if (Result != 0) {
  125. goto openptyEnd;
  126. }
  127. Result = unlockpt(MasterFile);
  128. if (Result != 0) {
  129. goto openptyEnd;
  130. }
  131. Result = ptsname_r(MasterFile, Path, sizeof(Path));
  132. if (Result != 0) {
  133. goto openptyEnd;
  134. }
  135. SlaveFile = open(Path, O_RDWR | O_NOCTTY);
  136. if (SlaveFile < 0) {
  137. goto openptyEnd;
  138. }
  139. if (Settings != NULL) {
  140. tcsetattr(SlaveFile, TCSAFLUSH, Settings);
  141. }
  142. if (WindowSize != NULL) {
  143. ioctl(SlaveFile, TIOCSWINSZ, WindowSize);
  144. }
  145. if (Name != NULL) {
  146. strcpy(Name, Path);
  147. }
  148. Result = 0;
  149. openptyEnd:
  150. if (Result != 0) {
  151. if (MasterFile >= 0) {
  152. close(MasterFile);
  153. MasterFile = -1;
  154. }
  155. if (SlaveFile >= 0) {
  156. close(SlaveFile);
  157. SlaveFile = -1;
  158. }
  159. }
  160. *Master = MasterFile;
  161. *Slave = SlaveFile;
  162. return Result;
  163. }
  164. LIBC_API
  165. int
  166. login_tty (
  167. int TerminalDescriptor
  168. )
  169. /*++
  170. Routine Description:
  171. This routine prepares for a login on the given terminal. It creates a new
  172. session, makes the given terminal descriptor the controlling terminal for
  173. the session, sets the terminal as standard input, output, and error, and
  174. closes the given descriptor.
  175. Arguments:
  176. TerminalDescriptor - Supplies the file descriptor of the terminal to start
  177. a login on.
  178. Return Value:
  179. 0 on success.
  180. -1 on failure, and errno will be set to contain more information.
  181. --*/
  182. {
  183. if (setsid() < 0) {
  184. return -1;
  185. }
  186. if (ioctl(TerminalDescriptor, TIOCSCTTY, 0) < 0) {
  187. return -1;
  188. }
  189. dup2(TerminalDescriptor, STDIN_FILENO);
  190. dup2(TerminalDescriptor, STDOUT_FILENO);
  191. dup2(TerminalDescriptor, STDERR_FILENO);
  192. if (TerminalDescriptor > STDERR_FILENO) {
  193. close(TerminalDescriptor);
  194. }
  195. return 0;
  196. }
  197. LIBC_API
  198. pid_t
  199. forkpty (
  200. int *Master,
  201. char *Name,
  202. const struct termios *Settings,
  203. const struct winsize *WindowSize
  204. )
  205. /*++
  206. Routine Description:
  207. This routine combines openpty, fork, and login_tty to create a new process
  208. wired up to a pseudo-terminal.
  209. Arguments:
  210. Master - Supplies a pointer where a file descriptor to the master will be
  211. returned on success. This is only returned in the parent.
  212. Name - Supplies an optional pointer where the name of the slave terminal
  213. will be returned on success. This buffer must be PATH_MAX size in bytes
  214. if supplied.
  215. Settings - Supplies an optional pointer to the settings to apply to the
  216. new terminal.
  217. WindowSize - Supplies an optional pointer to the window size to set in the
  218. new terminal.
  219. Return Value:
  220. Returns the pid of the forked child on success in the parent.
  221. 0 on success in the child.
  222. -1 on failure, and errno will be set to contain more information.
  223. --*/
  224. {
  225. pid_t Child;
  226. int MasterDescriptor;
  227. int Slave;
  228. if (openpty(&MasterDescriptor, &Slave, Name, Settings, WindowSize) == -1) {
  229. return -1;
  230. }
  231. Child = fork();
  232. if (Child < 0) {
  233. return -1;
  234. }
  235. //
  236. // If this is the child, make the new slave portion the controlling
  237. // terminal.
  238. //
  239. if (Child == 0) {
  240. close(MasterDescriptor);
  241. MasterDescriptor = -1;
  242. //
  243. // If login_tty fails to set the controlling terminal, then do the
  244. // rest of it as if it succeeded.
  245. //
  246. if (login_tty(Slave) < 0) {
  247. syslog(LOG_ERR, "forkpty: login_tty failed.\n");
  248. dup2(Slave, STDIN_FILENO);
  249. dup2(Slave, STDOUT_FILENO);
  250. dup2(Slave, STDERR_FILENO);
  251. if (Slave > STDERR_FILENO) {
  252. close(Slave);
  253. }
  254. }
  255. //
  256. // In the parent, close the slave.
  257. //
  258. } else {
  259. *Master = MasterDescriptor;
  260. close(Slave);
  261. }
  262. return Child;
  263. }
  264. LIBC_API
  265. int
  266. getpt (
  267. void
  268. )
  269. /*++
  270. Routine Description:
  271. This routine creates and opens a new pseudo-terminal master.
  272. Arguments:
  273. None.
  274. Return Value:
  275. Returns a file descriptor to the new terminal master device on success.
  276. -1 on failure, and errno will be set to contain more information.
  277. --*/
  278. {
  279. return posix_openpt(O_RDWR | O_NOCTTY);
  280. }
  281. LIBC_API
  282. int
  283. posix_openpt (
  284. int Flags
  285. )
  286. /*++
  287. Routine Description:
  288. This routine creates and opens a new pseudo-terminal master.
  289. Arguments:
  290. Flags - Supplies a bitfield of open flags governing the open. Only O_RDWR
  291. and O_NOCTTY are observed.
  292. Return Value:
  293. Returns a file descriptor to the new terminal master device on success.
  294. -1 on failure, and errno will be set to contain more information.
  295. --*/
  296. {
  297. PSTR Directory;
  298. int Error;
  299. HANDLE Handle;
  300. UINTN Index;
  301. CHAR MasterPath[PTY_PATH_MAX];
  302. UINTN MasterPathSize;
  303. ULONG OpenFlags;
  304. CHAR SlavePath[PTY_PATH_MAX];
  305. UINTN SlavePathSize;
  306. KSTATUS Status;
  307. Handle = INVALID_HANDLE;
  308. OpenFlags = 0;
  309. switch (Flags & O_ACCMODE) {
  310. case O_RDONLY:
  311. OpenFlags |= SYS_OPEN_FLAG_READ;
  312. break;
  313. case O_WRONLY:
  314. OpenFlags |= SYS_OPEN_FLAG_WRITE;
  315. break;
  316. case O_RDWR:
  317. OpenFlags |= SYS_OPEN_FLAG_READ | SYS_OPEN_FLAG_WRITE;
  318. break;
  319. default:
  320. break;
  321. }
  322. if ((Flags & O_NOCTTY) != 0) {
  323. OpenFlags |= SYS_OPEN_FLAG_NO_CONTROLLING_TERMINAL;
  324. }
  325. //
  326. // Figure out where to create the terminal at. Prefer /dev, then the
  327. // current working directory, then /tmp.
  328. //
  329. Directory = PTY_PREFERRED_DIRECTORY;
  330. if (access(Directory, W_OK) != 0) {
  331. Directory = PTY_PREFERRED_DIRECTORY2;
  332. if (access(Directory, W_OK) != 0) {
  333. Directory = PTY_FALLBACK_DIRECTORY;
  334. if (access(Directory, W_OK) != 0) {
  335. errno = EACCES;
  336. return -1;
  337. }
  338. }
  339. }
  340. //
  341. // Loop trying to create a terminal.
  342. //
  343. Error = EAGAIN;
  344. for (Index = 0; Index < PTY_MAX; Index += 1) {
  345. MasterPathSize = RtlPrintToString(MasterPath,
  346. sizeof(MasterPath),
  347. CharacterEncodingDefault,
  348. PTY_MASTER_PATH_FORMAT,
  349. Directory,
  350. Index);
  351. SlavePathSize = RtlPrintToString(SlavePath,
  352. sizeof(SlavePath),
  353. CharacterEncodingDefault,
  354. PTY_SLAVE_PATH_FORMAT,
  355. Directory,
  356. Index);
  357. Status = OsCreateTerminal(INVALID_HANDLE,
  358. INVALID_HANDLE,
  359. MasterPath,
  360. MasterPathSize + 1,
  361. SlavePath,
  362. SlavePathSize + 1,
  363. OpenFlags,
  364. PTY_INITIAL_PERMISSIONS,
  365. PTY_INITIAL_PERMISSIONS,
  366. &Handle);
  367. if (KSUCCESS(Status)) {
  368. Error = 0;
  369. break;
  370. } else if ((Status != STATUS_FILE_EXISTS) &&
  371. (Status != STATUS_ACCESS_DENIED)) {
  372. Error = ClConvertKstatusToErrorNumber(Status);
  373. break;
  374. }
  375. }
  376. if (Error != 0) {
  377. errno = Error;
  378. return -1;
  379. }
  380. return (int)(UINTN)Handle;
  381. }
  382. LIBC_API
  383. int
  384. grantpt (
  385. int Descriptor
  386. )
  387. /*++
  388. Routine Description:
  389. This routine changes the ownership and access permission of the slave
  390. pseudo-terminal associated with the given master pseudo-terminal file
  391. descriptor so that folks can open it.
  392. Arguments:
  393. Descriptor - Supplies the file descriptor of the master pseudo-terminal.
  394. Return Value:
  395. 0 on success.
  396. -1 on failure, and errno will be set to contain more information.
  397. --*/
  398. {
  399. struct group Group;
  400. char *GroupBuffer;
  401. size_t GroupBufferSize;
  402. struct group *GroupPointer;
  403. int OriginalError;
  404. uid_t RealUserId;
  405. int Result;
  406. CHAR SlaveName[PATH_MAX];
  407. struct stat Stat;
  408. gid_t TtyGroup;
  409. Result = ptsname_r(Descriptor, SlaveName, sizeof(SlaveName));
  410. if (Result == 0) {
  411. Result = stat(SlaveName, &Stat);
  412. }
  413. if (Result != 0) {
  414. OriginalError = errno;
  415. //
  416. // If the file descriptor is not a terminal, return EINVAL.
  417. //
  418. if ((fcntl(Descriptor, F_GETFD) != 0) && (errno == EBADF)) {
  419. return -1;
  420. }
  421. if (OriginalError == ENOTTY) {
  422. errno = EINVAL;
  423. } else {
  424. errno = OriginalError;
  425. }
  426. return -1;
  427. }
  428. //
  429. // Own the device.
  430. //
  431. RealUserId = getuid();
  432. if (Stat.st_uid != RealUserId) {
  433. Result = chown(SlaveName, RealUserId, Stat.st_gid);
  434. if (Result != 0) {
  435. return -1;
  436. }
  437. }
  438. //
  439. // Go look up the TTY group if not found already. If it could not be found,
  440. // set it to the current real group ID.
  441. //
  442. if (ClTtyGroup == (gid_t)-1) {
  443. GroupBufferSize = sysconf(_SC_GETGR_R_SIZE_MAX);
  444. GroupBuffer = malloc(GroupBufferSize);
  445. if (GroupBuffer != NULL) {
  446. GroupPointer = NULL;
  447. getgrnam_r(TTY_GROUP_NAME,
  448. &Group,
  449. GroupBuffer,
  450. GroupBufferSize,
  451. &GroupPointer);
  452. if (GroupPointer != NULL) {
  453. ClTtyGroup = GroupPointer->gr_gid;
  454. }
  455. free(GroupBuffer);
  456. }
  457. }
  458. TtyGroup = ClTtyGroup;
  459. if (TtyGroup == (gid_t)-1) {
  460. TtyGroup = getgid();
  461. }
  462. //
  463. // Change the terminal to belong to the group.
  464. //
  465. if (Stat.st_gid != TtyGroup) {
  466. Result = chown(SlaveName, RealUserId, TtyGroup);
  467. if (Result != 0) {
  468. return -1;
  469. }
  470. }
  471. //
  472. // Ensure the permissions are writable by the user and group.
  473. //
  474. if ((Stat.st_mode & ACCESSPERMS) != TTY_SLAVE_PERMISSIONS) {
  475. Result = chmod(SlaveName, TTY_SLAVE_PERMISSIONS);
  476. if (Result != 0) {
  477. return -1;
  478. }
  479. }
  480. return 0;
  481. }
  482. LIBC_API
  483. int
  484. unlockpt (
  485. int Descriptor
  486. )
  487. /*++
  488. Routine Description:
  489. This routine unlocks the slave side of the pseudo-terminal associated with
  490. the given master side file descriptor.
  491. Arguments:
  492. Descriptor - Supplies the open file descriptor to the master side of the
  493. terminal.
  494. Return Value:
  495. 0 on success.
  496. -1 on failure, and errno will be set to contain more information.
  497. --*/
  498. {
  499. if (!isatty(Descriptor)) {
  500. errno = ENOTTY;
  501. return -1;
  502. }
  503. return 0;
  504. }
  505. LIBC_API
  506. char *
  507. ptsname (
  508. int Descriptor
  509. )
  510. /*++
  511. Routine Description:
  512. This routine returns the name of the slave pseudoterminal associated
  513. with the given master file descriptor. This function is neither thread-safe
  514. nor reentrant.
  515. Arguments:
  516. Descriptor - Supplies the open file descriptor to the master side of the
  517. terminal.
  518. Return Value:
  519. Returns a pointer to a static area containing the name of the terminal on
  520. success. The caller must not modify or free this buffer, and it may be
  521. overwritten by subsequent calls to ptsname.
  522. NULL on failure, and errno will be set to contain more information.
  523. --*/
  524. {
  525. CHAR BigBuffer[PATH_MAX];
  526. int Result;
  527. Result = ptsname_r(Descriptor, BigBuffer, PATH_MAX);
  528. if (Result != 0) {
  529. return NULL;
  530. }
  531. if (ClTerminalSlaveName != NULL) {
  532. free(ClTerminalSlaveName);
  533. }
  534. ClTerminalSlaveName = strdup(BigBuffer);
  535. return ClTerminalSlaveName;
  536. }
  537. LIBC_API
  538. int
  539. ptsname_r (
  540. int Descriptor,
  541. char *Buffer,
  542. size_t BufferSize
  543. )
  544. /*++
  545. Routine Description:
  546. This routine returns the name of the slave pseudoterminal associated
  547. with the given master file descriptor. This is the reentrant version of the
  548. ptsname function.
  549. Arguments:
  550. Descriptor - Supplies the open file descriptor to the master side of the
  551. terminal.
  552. Buffer - Supplies a pointer where the name will be returned on success.
  553. BufferSize - Supplies the size of the given buffer in bytes.
  554. Return Value:
  555. 0 on success.
  556. -1 on failure, and errno will be set to contain more information.
  557. --*/
  558. {
  559. UINTN Size;
  560. KSTATUS Status;
  561. if (!isatty(Descriptor)) {
  562. errno = ENOTTY;
  563. return -1;
  564. }
  565. Size = BufferSize;
  566. Status = OsGetFilePath((HANDLE)(UINTN)Descriptor, Buffer, &Size);
  567. if (!KSUCCESS(Status)) {
  568. errno = ClConvertKstatusToErrorNumber(Status);
  569. return -1;
  570. }
  571. //
  572. // The path had better be a least a character, 'm', and a null terminator.
  573. //
  574. if (Size < 3) {
  575. errno = EINVAL;
  576. return -1;
  577. }
  578. //
  579. // The only difference (by C library convention) between master and slave
  580. // terminals is a letter m on the end of the master. Chop that off to get
  581. // the slave path.
  582. //
  583. if (Buffer[Size - 2] == 'm') {
  584. Buffer[Size - 2] = '\0';
  585. }
  586. return 0;
  587. }
  588. //
  589. // --------------------------------------------------------- Internal Functions
  590. //