1
0

line.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  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. line.c
  9. Abstract:
  10. This module implements line processing functions.
  11. Author:
  12. Evan Green 9-Mar-2015
  13. Environment:
  14. User Mode C Library
  15. --*/
  16. //
  17. // ------------------------------------------------------------------- Includes
  18. //
  19. #include "libcp.h"
  20. #include <assert.h>
  21. #include <errno.h>
  22. #include <paths.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <termios.h>
  26. #include <unistd.h>
  27. //
  28. // ---------------------------------------------------------------- Definitions
  29. //
  30. #define GETLINE_INITIAL_BUFFER_SIZE 64
  31. //
  32. // ------------------------------------------------------ Data Type Definitions
  33. //
  34. //
  35. // ----------------------------------------------- Internal Function Prototypes
  36. //
  37. void
  38. ClpGetpassSignalHandler (
  39. int Signal
  40. );
  41. //
  42. // -------------------------------------------------------------------- Globals
  43. //
  44. char *ClGetpassBuffer = NULL;
  45. size_t ClGetpassBufferSize = 0;
  46. int *ClGetpassSignals = NULL;
  47. //
  48. // ------------------------------------------------------------------ Functions
  49. //
  50. LIBC_API
  51. char *
  52. getpass (
  53. const char *Prompt
  54. )
  55. /*++
  56. Routine Description:
  57. This routine reads outputs the given prompt, and reads in a line of input
  58. without echoing it. This routine attempts to use the process' controlling
  59. terminal, or stdin/stderr otherwise. This routine is neither thread-safe
  60. nor reentrant.
  61. Arguments:
  62. Prompt - Supplies a pointer to the prompt to print.
  63. Return Value:
  64. Returns a pointer to the entered input on success. If this is a password,
  65. the caller should be sure to clear this buffer out as soon as possible.
  66. NULL on failure.
  67. --*/
  68. {
  69. ssize_t BytesRead;
  70. char Character;
  71. int DescriptorIn;
  72. FILE *FileIn;
  73. size_t LineSize;
  74. struct sigaction NewAction;
  75. void *NewBuffer;
  76. size_t NewBufferSize;
  77. struct termios NewSettings;
  78. struct termios OriginalSettings;
  79. PSTR ResultBuffer;
  80. struct sigaction SaveAlarm;
  81. struct sigaction SaveHup;
  82. struct sigaction SaveInt;
  83. struct sigaction SavePipe;
  84. struct sigaction SaveQuit;
  85. struct sigaction SaveTerm;
  86. struct sigaction SaveTstop;
  87. struct sigaction SaveTtin;
  88. struct sigaction SaveTtou;
  89. int Signal;
  90. int Signals[NSIG];
  91. ResultBuffer = NULL;
  92. memset(Signals, 0, sizeof(Signals));
  93. ClGetpassSignals = Signals;
  94. FileIn = fopen(_PATH_TTY, "w+");
  95. if (FileIn == NULL) {
  96. return NULL;
  97. }
  98. DescriptorIn = fileno(FileIn);
  99. //
  100. // Turn off echoing.
  101. //
  102. if ((DescriptorIn < 0) ||
  103. (tcgetattr(DescriptorIn, &OriginalSettings) != 0)) {
  104. fclose(FileIn);
  105. return NULL;
  106. }
  107. memcpy(&NewSettings, &OriginalSettings, sizeof(struct termios));
  108. NewSettings.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
  109. if (tcsetattr(DescriptorIn, TCSAFLUSH, &NewSettings) != 0) {
  110. goto getpassEnd;
  111. }
  112. //
  113. // Handle all signals so that the terminal settings can be put back.
  114. //
  115. sigemptyset(&(NewAction.sa_mask));
  116. NewAction.sa_flags = 0;
  117. NewAction.sa_handler = ClpGetpassSignalHandler;
  118. sigaction(SIGALRM, &NewAction, &SaveAlarm);
  119. sigaction(SIGHUP, &NewAction, &SaveHup);
  120. sigaction(SIGINT, &NewAction, &SaveInt);
  121. sigaction(SIGPIPE, &NewAction, &SavePipe);
  122. sigaction(SIGQUIT, &NewAction, &SaveQuit);
  123. sigaction(SIGTERM, &NewAction, &SaveTerm);
  124. sigaction(SIGTSTP, &NewAction, &SaveTstop);
  125. sigaction(SIGTTIN, &NewAction, &SaveTtin);
  126. sigaction(SIGTTOU, &NewAction, &SaveTtou);
  127. //
  128. // Print the prompt.
  129. //
  130. fprintf(stderr, Prompt);
  131. fflush(stderr);
  132. //
  133. // Loop reading characters from the input.
  134. //
  135. LineSize = 0;
  136. BytesRead = 0;
  137. while (TRUE) {
  138. for (Signal = 0; Signal < NSIG; Signal += 1) {
  139. if (ClGetpassSignals[Signal] != 0) {
  140. break;
  141. }
  142. }
  143. if (Signal != NSIG) {
  144. break;
  145. }
  146. BytesRead = read(DescriptorIn, &Character, 1);
  147. if ((BytesRead < 0) && (errno == EINTR)) {
  148. continue;
  149. }
  150. if (BytesRead <= 0) {
  151. break;
  152. }
  153. //
  154. // Reallocate the buffer if needed.
  155. //
  156. if (LineSize + 1 >= ClGetpassBufferSize) {
  157. if (ClGetpassBufferSize == 0) {
  158. NewBufferSize = GETLINE_INITIAL_BUFFER_SIZE;
  159. } else {
  160. NewBufferSize = ClGetpassBufferSize * 2;
  161. }
  162. NewBuffer = malloc(NewBufferSize);
  163. //
  164. // Whether or not the allocation succeeded, zero out the previous
  165. // buffer to avoid leaking potential passwords.
  166. //
  167. if (ClGetpassBufferSize != 0) {
  168. if (NewBuffer != NULL) {
  169. memcpy(NewBuffer, ClGetpassBuffer, ClGetpassBufferSize);
  170. }
  171. SECURITY_ZERO(ClGetpassBuffer, ClGetpassBufferSize);
  172. free(ClGetpassBuffer);
  173. ClGetpassBuffer = NULL;
  174. ClGetpassBufferSize = 0;
  175. }
  176. if (NewBuffer == NULL) {
  177. LineSize = 0;
  178. break;
  179. } else {
  180. ClGetpassBuffer = NewBuffer;
  181. ClGetpassBufferSize = NewBufferSize;
  182. }
  183. }
  184. if ((Character == '\r') || (Character == '\n')) {
  185. break;
  186. }
  187. //
  188. // Control-C cancels early.
  189. //
  190. if (Character == 3) {
  191. goto getpassEnd;
  192. }
  193. //
  194. // Add the character to the buffer.
  195. //
  196. ClGetpassBuffer[LineSize] = Character;
  197. LineSize += 1;
  198. }
  199. if (BytesRead >= 0) {
  200. if ((BytesRead > 0) || (LineSize > 0)) {
  201. assert(LineSize + 1 < ClGetpassBufferSize);
  202. ClGetpassBuffer[LineSize] = '\0';
  203. LineSize += 1;
  204. } else {
  205. BytesRead = -1;
  206. }
  207. fputc('\n', stderr);
  208. }
  209. ResultBuffer = ClGetpassBuffer;
  210. getpassEnd:
  211. //
  212. // If the result was not successful but there's a partial buffer, zero it
  213. // out.
  214. //
  215. if ((ResultBuffer == NULL) && (ClGetpassBufferSize != 0)) {
  216. SECURITY_ZERO(ClGetpassBuffer, ClGetpassBufferSize);
  217. }
  218. //
  219. // Restore the original terminal settings.
  220. //
  221. tcsetattr(DescriptorIn, TCSAFLUSH, &OriginalSettings);
  222. fclose(FileIn);
  223. //
  224. // Restore the original signal handlers.
  225. //
  226. sigaction(SIGALRM, &SaveAlarm, NULL);
  227. sigaction(SIGHUP, &SaveHup, NULL);
  228. sigaction(SIGINT, &SaveInt, NULL);
  229. sigaction(SIGPIPE, &SavePipe, NULL);
  230. sigaction(SIGQUIT, &SaveQuit, NULL);
  231. sigaction(SIGTERM, &SaveTerm, NULL);
  232. sigaction(SIGTSTP, &SaveTstop, NULL);
  233. sigaction(SIGTTIN, &SaveTtin, NULL);
  234. sigaction(SIGTTOU, &SaveTtou, NULL);
  235. //
  236. // Replay any signals that were sent during the read.
  237. //
  238. for (Signal = 0; Signal < NSIG; Signal += 1) {
  239. while (ClGetpassSignals[Signal] != 0) {
  240. kill(getpid(), Signal);
  241. ClGetpassSignals[Signal] -= 1;
  242. }
  243. }
  244. ClGetpassSignals = NULL;
  245. return ResultBuffer;
  246. }
  247. LIBC_API
  248. ssize_t
  249. getline (
  250. char **LinePointer,
  251. size_t *Size,
  252. FILE *Stream
  253. )
  254. /*++
  255. Routine Description:
  256. This routine reads an entire line from the given stream. This routine will
  257. allocate or reallocate the given buffer so that the buffer is big enough.
  258. Arguments:
  259. LinePointer - Supplies a pointer that on input contains an optional pointer
  260. to a buffer to use to read the line. If the buffer ends up being not
  261. big enough, it will be reallocated. If no buffer is supplied, one will
  262. be allocated. On output, contains a pointer to the buffer containing
  263. the read line on success.
  264. Size - Supplies a pointer that on input contains the size in bytes of the
  265. supplied line pointer. On output, this value will be updated to contain
  266. the size of the buffer returned in the line buffer parameter.
  267. Stream - Supplies the stream to read the line from.
  268. Return Value:
  269. On success, returns the number of characters read, including the delimiter
  270. character, but not including the null terminator.
  271. Returns -1 on failure (including an end of file condition), and errno will
  272. be set to contain more information.
  273. --*/
  274. {
  275. return getdelim(LinePointer, Size, '\n', Stream);
  276. }
  277. LIBC_API
  278. ssize_t
  279. getdelim (
  280. char **LinePointer,
  281. size_t *Size,
  282. int Delimiter,
  283. FILE *Stream
  284. )
  285. /*++
  286. Routine Description:
  287. This routine reads an entire line from the given stream, delimited by the
  288. given delimiter character. This routine will allocate or reallocate the
  289. given buffer so that the buffer is big enough.
  290. Arguments:
  291. LinePointer - Supplies a pointer that on input contains an optional pointer
  292. to a buffer to use to read the line. If the buffer ends up being not
  293. big enough, it will be reallocated. If no buffer is supplied, one will
  294. be allocated. On output, contains a pointer to the buffer containing
  295. the read line on success.
  296. Size - Supplies a pointer that on input contains the size in bytes of the
  297. supplied line pointer. On output, this value will be updated to contain
  298. the size of the buffer returned in the line buffer parameter.
  299. Delimiter - Supplies the delimiter to split the line on.
  300. Stream - Supplies the stream to read the line from.
  301. Return Value:
  302. On success, returns the number of characters read, including the delimiter
  303. character, but not including the null terminator.
  304. Returns -1 on failure (including an end of file condition), and errno will
  305. be set to contain more information.
  306. --*/
  307. {
  308. int Character;
  309. size_t LineSize;
  310. char *NewBuffer;
  311. size_t NewSize;
  312. LineSize = 0;
  313. if ((LinePointer == NULL) || (Size == NULL)) {
  314. errno = EINVAL;
  315. return -1;
  316. }
  317. if (Stream == NULL) {
  318. errno = EBADF;
  319. return -1;
  320. }
  321. //
  322. // Allocate an initial buffer if the caller passed one that is NULL or
  323. // too small.
  324. //
  325. if ((*LinePointer == NULL) || (*Size < GETLINE_INITIAL_BUFFER_SIZE)) {
  326. NewBuffer = realloc(*LinePointer, GETLINE_INITIAL_BUFFER_SIZE);
  327. if (NewBuffer == NULL) {
  328. return -1;
  329. }
  330. *LinePointer = NewBuffer;
  331. *Size = GETLINE_INITIAL_BUFFER_SIZE;
  332. }
  333. while (TRUE) {
  334. Character = fgetc(Stream);
  335. if (Character == EOF) {
  336. if (LineSize != 0) {
  337. break;
  338. }
  339. return -1;
  340. }
  341. if (LineSize + 2 > *Size) {
  342. assert(*Size != 0);
  343. NewSize = *Size;
  344. while (NewSize < LineSize + 2) {
  345. NewSize *= 2;
  346. }
  347. NewBuffer = realloc(*LinePointer, NewSize);
  348. if (NewBuffer == NULL) {
  349. return -1;
  350. }
  351. *LinePointer = NewBuffer;
  352. *Size = NewSize;
  353. }
  354. (*LinePointer)[LineSize] = Character;
  355. LineSize += 1;
  356. if (Character == Delimiter) {
  357. break;
  358. }
  359. }
  360. assert(*Size > LineSize);
  361. (*LinePointer)[LineSize] = '\0';
  362. return LineSize;
  363. }
  364. //
  365. // --------------------------------------------------------- Internal Functions
  366. //
  367. void
  368. ClpGetpassSignalHandler (
  369. int Signal
  370. )
  371. /*++
  372. Routine Description:
  373. This routine is the signal handler installed during the getpass function.
  374. It simply tracks signals for replay later.
  375. Arguments:
  376. Signal - Supplies the signal number that fired.
  377. Return Value:
  378. None.
  379. --*/
  380. {
  381. assert((ClGetpassSignals != NULL) && (Signal < NSIG));
  382. ClGetpassSignals[Signal] += 1;
  383. return;
  384. }