tty_inq.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507
  1. /*
  2. * Copyright (c) 2006-2023, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2023-11-13 Shell init ver.
  9. */
  10. #include "../bsd_porting.h"
  11. #include "../terminal.h"
  12. /*-
  13. * SPDX-License-Identifier: BSD-2-Clause
  14. *
  15. * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
  16. * All rights reserved.
  17. *
  18. * Portions of this software were developed under sponsorship from Snow
  19. * B.V., the Netherlands.
  20. *
  21. * Redistribution and use in source and binary forms, with or without
  22. * modification, are permitted provided that the following conditions
  23. * are met:
  24. * 1. Redistributions of source code must retain the above copyright
  25. * notice, this list of conditions and the following disclaimer.
  26. * 2. Redistributions in binary form must reproduce the above copyright
  27. * notice, this list of conditions and the following disclaimer in the
  28. * documentation and/or other materials provided with the distribution.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  31. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  32. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  34. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  35. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  36. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  37. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  38. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  39. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  40. * SUCH DAMAGE.
  41. */
  42. /*
  43. * TTY input queue buffering.
  44. *
  45. * Unlike the output queue, the input queue has more features that are
  46. * needed to properly implement various features offered by the TTY
  47. * interface:
  48. *
  49. * - Data can be removed from the tail of the queue, which is used to
  50. * implement backspace.
  51. * - Once in a while, input has to be `canonicalized'. When ICANON is
  52. * turned on, this will be done after a CR has been inserted.
  53. * Otherwise, it should be done after any character has been inserted.
  54. * - The input queue can store one bit per byte, called the quoting bit.
  55. * This bit is used by TTYDISC to make backspace work on quoted
  56. * characters.
  57. *
  58. * In most cases, there is probably less input than output, so unlike
  59. * the outq, we'll stick to 128 byte blocks here.
  60. */
  61. static int ttyinq_flush_secure = 1;
  62. #define TTYINQ_QUOTESIZE (TTYINQ_DATASIZE / BMSIZE)
  63. #define BMSIZE 32
  64. #define GETBIT(tib, boff) \
  65. ((tib)->tib_quotes[(boff) / BMSIZE] & (1 << ((boff) % BMSIZE)))
  66. #define SETBIT(tib, boff) \
  67. ((tib)->tib_quotes[(boff) / BMSIZE] |= (1 << ((boff) % BMSIZE)))
  68. #define CLRBIT(tib, boff) \
  69. ((tib)->tib_quotes[(boff) / BMSIZE] &= ~(1 << ((boff) % BMSIZE)))
  70. struct ttyinq_block
  71. {
  72. struct ttyinq_block *tib_prev;
  73. struct ttyinq_block *tib_next;
  74. uint32_t tib_quotes[TTYINQ_QUOTESIZE];
  75. char tib_data[TTYINQ_DATASIZE];
  76. };
  77. static uma_zone_t ttyinq_zone;
  78. #define TTYINQ_INSERT_TAIL(ti, tib) \
  79. do \
  80. { \
  81. if (ti->ti_end == 0) \
  82. { \
  83. tib->tib_prev = NULL; \
  84. tib->tib_next = ti->ti_firstblock; \
  85. ti->ti_firstblock = tib; \
  86. } \
  87. else \
  88. { \
  89. tib->tib_prev = ti->ti_lastblock; \
  90. tib->tib_next = ti->ti_lastblock->tib_next; \
  91. ti->ti_lastblock->tib_next = tib; \
  92. } \
  93. if (tib->tib_next != NULL) \
  94. tib->tib_next->tib_prev = tib; \
  95. ti->ti_nblocks++; \
  96. } while (0)
  97. #define TTYINQ_REMOVE_HEAD(ti) \
  98. do \
  99. { \
  100. ti->ti_firstblock = ti->ti_firstblock->tib_next; \
  101. if (ti->ti_firstblock != NULL) \
  102. ti->ti_firstblock->tib_prev = NULL; \
  103. ti->ti_nblocks--; \
  104. } while (0)
  105. #define TTYINQ_RECYCLE(ti, tib) \
  106. do \
  107. { \
  108. if (ti->ti_quota <= ti->ti_nblocks) \
  109. uma_zfree(ttyinq_zone, tib); \
  110. else \
  111. TTYINQ_INSERT_TAIL(ti, tib); \
  112. } while (0)
  113. int ttyinq_setsize(struct ttyinq *ti, struct lwp_tty *tp, size_t size)
  114. {
  115. struct ttyinq_block *tib;
  116. ti->ti_quota = howmany(size, TTYINQ_DATASIZE);
  117. while (ti->ti_quota > ti->ti_nblocks)
  118. {
  119. /*
  120. * List is getting bigger.
  121. * Add new blocks to the tail of the list.
  122. *
  123. * We must unlock the TTY temporarily, because we need
  124. * to allocate memory. This won't be a problem, because
  125. * in the worst case, another thread ends up here, which
  126. * may cause us to allocate too many blocks, but this
  127. * will be caught by the loop below.
  128. */
  129. tty_unlock(tp);
  130. tib = uma_zalloc(ttyinq_zone, M_WAITOK);
  131. tty_lock(tp);
  132. if (tty_gone(tp))
  133. {
  134. uma_zfree(ttyinq_zone, tib);
  135. return -ENXIO;
  136. }
  137. TTYINQ_INSERT_TAIL(ti, tib);
  138. }
  139. return 0;
  140. }
  141. void ttyinq_free(struct ttyinq *ti)
  142. {
  143. struct ttyinq_block *tib;
  144. ttyinq_flush(ti);
  145. ti->ti_quota = 0;
  146. while ((tib = ti->ti_firstblock) != NULL)
  147. {
  148. TTYINQ_REMOVE_HEAD(ti);
  149. uma_zfree(ttyinq_zone, tib);
  150. }
  151. MPASS(ti->ti_nblocks == 0);
  152. }
  153. int ttyinq_read_uio(struct ttyinq *ti, struct lwp_tty *tp, struct uio *uio,
  154. size_t rlen, size_t flen)
  155. {
  156. MPASS(rlen <= uio->uio_resid);
  157. while (rlen > 0)
  158. {
  159. int error;
  160. struct ttyinq_block *tib;
  161. size_t cbegin, cend, clen;
  162. /* See if there still is data. */
  163. if (ti->ti_begin == ti->ti_linestart)
  164. return 0;
  165. tib = ti->ti_firstblock;
  166. if (tib == NULL)
  167. return 0;
  168. /*
  169. * The end address should be the lowest of these three:
  170. * - The write pointer
  171. * - The blocksize - we can't read beyond the block
  172. * - The end address if we could perform the full read
  173. */
  174. cbegin = ti->ti_begin;
  175. cend = MIN(MIN(ti->ti_linestart, ti->ti_begin + rlen), TTYINQ_DATASIZE);
  176. clen = cend - cbegin;
  177. MPASS(clen >= flen);
  178. rlen -= clen;
  179. /*
  180. * We can prevent buffering in some cases:
  181. * - We need to read the block until the end.
  182. * - We don't need to read the block until the end, but
  183. * there is no data beyond it, which allows us to move
  184. * the write pointer to a new block.
  185. */
  186. if (cend == TTYINQ_DATASIZE || cend == ti->ti_end)
  187. {
  188. /*
  189. * Fast path: zero copy. Remove the first block,
  190. * so we can unlock the TTY temporarily.
  191. */
  192. TTYINQ_REMOVE_HEAD(ti);
  193. ti->ti_begin = 0;
  194. /*
  195. * Because we remove the first block, we must
  196. * fix up the block offsets.
  197. */
  198. #define CORRECT_BLOCK(t) \
  199. do \
  200. { \
  201. if (t <= TTYINQ_DATASIZE) \
  202. t = 0; \
  203. else \
  204. t -= TTYINQ_DATASIZE; \
  205. } while (0)
  206. CORRECT_BLOCK(ti->ti_linestart);
  207. CORRECT_BLOCK(ti->ti_reprint);
  208. CORRECT_BLOCK(ti->ti_end);
  209. #undef CORRECT_BLOCK
  210. /*
  211. * Temporary unlock and copy the data to
  212. * userspace. We may need to flush trailing
  213. * bytes, like EOF characters.
  214. */
  215. tty_unlock(tp);
  216. error = uiomove(tib->tib_data + cbegin, clen - flen, uio);
  217. tty_lock(tp);
  218. /* Block can now be readded to the list. */
  219. TTYINQ_RECYCLE(ti, tib);
  220. }
  221. else
  222. {
  223. char ob[TTYINQ_DATASIZE - 1];
  224. /*
  225. * Slow path: store data in a temporary buffer.
  226. */
  227. memcpy(ob, tib->tib_data + cbegin, clen - flen);
  228. ti->ti_begin += clen;
  229. MPASS(ti->ti_begin < TTYINQ_DATASIZE);
  230. /* Temporary unlock and copy the data to userspace. */
  231. tty_unlock(tp);
  232. error = uiomove(ob, clen - flen, uio);
  233. tty_lock(tp);
  234. }
  235. if (error != 0)
  236. return error;
  237. if (tty_gone(tp))
  238. return -ENXIO;
  239. }
  240. return 0;
  241. }
  242. rt_inline void ttyinq_set_quotes(struct ttyinq_block *tib, size_t offset,
  243. size_t length, int value)
  244. {
  245. if (value)
  246. {
  247. /* Set the bits. */
  248. for (; length > 0; length--, offset++) SETBIT(tib, offset);
  249. }
  250. else
  251. {
  252. /* Unset the bits. */
  253. for (; length > 0; length--, offset++) CLRBIT(tib, offset);
  254. }
  255. }
  256. size_t ttyinq_write(struct ttyinq *ti, const void *buf, size_t nbytes,
  257. int quote)
  258. {
  259. const char *cbuf = buf;
  260. struct ttyinq_block *tib;
  261. unsigned int boff;
  262. size_t l;
  263. while (nbytes > 0)
  264. {
  265. boff = ti->ti_end % TTYINQ_DATASIZE;
  266. if (ti->ti_end == 0)
  267. {
  268. /* First time we're being used or drained. */
  269. MPASS(ti->ti_begin == 0);
  270. tib = ti->ti_firstblock;
  271. if (tib == NULL)
  272. {
  273. /* Queue has no blocks. */
  274. break;
  275. }
  276. ti->ti_lastblock = tib;
  277. }
  278. else if (boff == 0)
  279. {
  280. /* We reached the end of this block on last write. */
  281. tib = ti->ti_lastblock->tib_next;
  282. if (tib == NULL)
  283. {
  284. /* We've reached the watermark. */
  285. break;
  286. }
  287. ti->ti_lastblock = tib;
  288. }
  289. else
  290. {
  291. tib = ti->ti_lastblock;
  292. }
  293. /* Don't copy more than was requested. */
  294. l = MIN(nbytes, TTYINQ_DATASIZE - boff);
  295. MPASS(l > 0);
  296. memcpy(tib->tib_data + boff, cbuf, l);
  297. /* Set the quoting bits for the proper region. */
  298. ttyinq_set_quotes(tib, boff, l, quote);
  299. cbuf += l;
  300. nbytes -= l;
  301. ti->ti_end += l;
  302. }
  303. return (cbuf - (const char *)buf);
  304. }
  305. int ttyinq_write_nofrag(struct ttyinq *ti, const void *buf, size_t nbytes,
  306. int quote)
  307. {
  308. size_t ret __unused;
  309. if (ttyinq_bytesleft(ti) < nbytes)
  310. return -1;
  311. /* We should always be able to write it back. */
  312. ret = ttyinq_write(ti, buf, nbytes, quote);
  313. MPASS(ret == nbytes);
  314. return 0;
  315. }
  316. void ttyinq_canonicalize(struct ttyinq *ti)
  317. {
  318. ti->ti_linestart = ti->ti_reprint = ti->ti_end;
  319. ti->ti_startblock = ti->ti_reprintblock = ti->ti_lastblock;
  320. }
  321. size_t ttyinq_findchar(struct ttyinq *ti, const char *breakc, size_t maxlen,
  322. char *lastc)
  323. {
  324. struct ttyinq_block *tib = ti->ti_firstblock;
  325. unsigned int boff = ti->ti_begin;
  326. unsigned int bend =
  327. MIN(MIN(TTYINQ_DATASIZE, ti->ti_linestart), ti->ti_begin + maxlen);
  328. MPASS(maxlen > 0);
  329. if (tib == NULL)
  330. return 0;
  331. while (boff < bend)
  332. {
  333. if (strchr(breakc, tib->tib_data[boff]) && !GETBIT(tib, boff))
  334. {
  335. *lastc = tib->tib_data[boff];
  336. return (boff - ti->ti_begin + 1);
  337. }
  338. boff++;
  339. }
  340. /* Not found - just process the entire block. */
  341. return (bend - ti->ti_begin);
  342. }
  343. void ttyinq_flush(struct ttyinq *ti)
  344. {
  345. struct ttyinq_block *tib;
  346. ti->ti_begin = 0;
  347. ti->ti_linestart = 0;
  348. ti->ti_reprint = 0;
  349. ti->ti_end = 0;
  350. /* Zero all data in the input queue to get rid of passwords. */
  351. if (ttyinq_flush_secure)
  352. {
  353. for (tib = ti->ti_firstblock; tib != NULL; tib = tib->tib_next)
  354. memset(&tib->tib_data, 0, sizeof tib->tib_data);
  355. }
  356. }
  357. int ttyinq_peekchar(struct ttyinq *ti, char *c, int *quote)
  358. {
  359. unsigned int boff;
  360. struct ttyinq_block *tib = ti->ti_lastblock;
  361. if (ti->ti_linestart == ti->ti_end)
  362. return -1;
  363. MPASS(ti->ti_end > 0);
  364. boff = (ti->ti_end - 1) % TTYINQ_DATASIZE;
  365. *c = tib->tib_data[boff];
  366. *quote = GETBIT(tib, boff);
  367. return 0;
  368. }
  369. void ttyinq_unputchar(struct ttyinq *ti)
  370. {
  371. MPASS(ti->ti_linestart < ti->ti_end);
  372. if (--ti->ti_end % TTYINQ_DATASIZE == 0)
  373. {
  374. /* Roll back to the previous block. */
  375. ti->ti_lastblock = ti->ti_lastblock->tib_prev;
  376. /*
  377. * This can only fail if we are unputchar()'ing the
  378. * first character in the queue.
  379. */
  380. MPASS((ti->ti_lastblock == NULL) == (ti->ti_end == 0));
  381. }
  382. }
  383. void ttyinq_reprintpos_set(struct ttyinq *ti)
  384. {
  385. ti->ti_reprint = ti->ti_end;
  386. ti->ti_reprintblock = ti->ti_lastblock;
  387. }
  388. void ttyinq_reprintpos_reset(struct ttyinq *ti)
  389. {
  390. ti->ti_reprint = ti->ti_linestart;
  391. ti->ti_reprintblock = ti->ti_startblock;
  392. }
  393. static void ttyinq_line_iterate(struct ttyinq *ti,
  394. ttyinq_line_iterator_t *iterator, void *data,
  395. unsigned int offset, struct ttyinq_block *tib)
  396. {
  397. unsigned int boff;
  398. /* Use the proper block when we're at the queue head. */
  399. if (offset == 0)
  400. tib = ti->ti_firstblock;
  401. /* Iterate all characters and call the iterator function. */
  402. for (; offset < ti->ti_end; offset++)
  403. {
  404. boff = offset % TTYINQ_DATASIZE;
  405. MPASS(tib != NULL);
  406. /* Call back the iterator function. */
  407. iterator(data, tib->tib_data[boff], GETBIT(tib, boff));
  408. /* Last byte iterated - go to the next block. */
  409. if (boff == TTYINQ_DATASIZE - 1)
  410. tib = tib->tib_next;
  411. }
  412. }
  413. void ttyinq_line_iterate_from_linestart(struct ttyinq *ti,
  414. ttyinq_line_iterator_t *iterator,
  415. void *data)
  416. {
  417. ttyinq_line_iterate(ti, iterator, data, ti->ti_linestart,
  418. ti->ti_startblock);
  419. }
  420. void ttyinq_line_iterate_from_reprintpos(struct ttyinq *ti,
  421. ttyinq_line_iterator_t *iterator,
  422. void *data)
  423. {
  424. ttyinq_line_iterate(ti, iterator, data, ti->ti_reprint,
  425. ti->ti_reprintblock);
  426. }
  427. static int ttyinq_startup(void)
  428. {
  429. ttyinq_zone = uma_zcreate("ttyinq", sizeof(struct ttyinq_block), NULL, NULL,
  430. NULL, NULL, UMA_ALIGN_PTR, 0);
  431. return 0;
  432. }
  433. INIT_PREV_EXPORT(ttyinq_startup);
  434. #if 0
  435. SYSINIT(ttyinq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyinq_startup, NULL);
  436. #endif