pty.c 150 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. pty.c
  5. Abstract:
  6. This module implements terminal support.
  7. Author:
  8. Evan Green 10-May-2013
  9. Environment:
  10. Kernel
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/kernel.h>
  16. #include <minoca/termlib.h>
  17. #include "iop.h"
  18. //
  19. // ---------------------------------------------------------------- Definitions
  20. //
  21. #define TERMINAL_ALLOCATION_TAG 0x216D7254 // '!mrT'
  22. #define TERMINAL_INITIAL_PERMISSIONS \
  23. (FILE_PERMISSION_USER_READ | FILE_PERMISSION_USER_WRITE | \
  24. FILE_PERMISSION_GROUP_READ | FILE_PERMISSION_GROUP_WRITE)
  25. #define TERMINAL_DIRECTORY_NAME "Terminal"
  26. #define TERMINAL_MASTER_NAME_FORMAT "Master%X"
  27. #define TERMINAL_SLAVE_NAME_FORMAT "Slave%X"
  28. #define TERMINAL_MAX_NAME_LENGTH 23
  29. #define TERMINAL_MAX_COMMAND_HISTORY 50
  30. #define TERMINAL_MAX_CANONICAL_OUTPUT 8
  31. //
  32. // Define the number of lines to scroll in canonical mode when page up/down
  33. // is seen.
  34. //
  35. #define TERMINAL_SCROLL_LINE_COUNT 5
  36. //
  37. // Define terminal limits. The input queue length must always be at least the
  38. // max canonical length since the line gets dumped into the input queue.
  39. //
  40. #define TERMINAL_INPUT_BUFFER_SIZE 512
  41. #define TERMINAL_CANONICAL_BUFFER_SIZE (TERMINAL_INPUT_BUFFER_SIZE - 1)
  42. #define TERMINAL_OUTPUT_BUFFER_SIZE 256
  43. //
  44. // Default control characters.
  45. //
  46. #define TERMINAL_DEFAULT_END_OF_FILE 0x04
  47. #define TERMINAL_DEFAULT_END_OF_LINE 0x00
  48. #define TERMINAL_DEFAULT_ERASE 0x7F
  49. #define TERMINAL_DEFAULT_INTERRUPT 0x03
  50. #define TERMINAL_DEFAULT_KILL 0x15
  51. #define TERMINAL_DEFAULT_QUIT 0x1C
  52. #define TERMINAL_DEFAULT_SUSPEND 0x1A
  53. #define TERMINAL_DEFAULT_START 0x11
  54. #define TERMINAL_DEFAULT_STOP 0x13
  55. //
  56. // Define the default baud rate terminals come up in.
  57. //
  58. #define TERMINAL_DEFAULT_BAUD_RATE 115200
  59. //
  60. // Define the default window size that terminals get initialized to.
  61. //
  62. #define TERMINAL_DEFAULT_ROWS 25
  63. #define TERMINAL_DEFAULT_COLUMNS 80
  64. //
  65. // Define terminal flags.
  66. //
  67. #define TERMINAL_FLAG_VIRGIN_LINE 0x00000001
  68. #define TERMINAL_FLAG_UNEDITED_LINE 0x0000002
  69. #define TERMINAL_FLAG_FAIL_OPENS 0x00000004
  70. //
  71. // Define the invalid session and process group IDs.
  72. //
  73. #define TERMINAL_INVALID_SESSION -1
  74. #define TERMINAL_INVALID_PROCESS_GROUP -1
  75. #define TERMINAL_POLL_ERRORS (POLL_EVENT_ERROR | POLL_EVENT_DISCONNECTED)
  76. //
  77. // --------------------------------------------------------------------- Macros
  78. //
  79. //
  80. // The terminal master is considered open if it has more than 1 reference. An
  81. // initial reference is taken upon creation, but that does not count towards
  82. // being opened.
  83. //
  84. #define IO_IS_TERMINAL_MASTER_OPEN(_Terminal) \
  85. ((_Terminal)->MasterReferenceCount > 1)
  86. //
  87. // ------------------------------------------------------ Data Type Definitions
  88. //
  89. typedef struct _TERMINAL_SLAVE TERMINAL_SLAVE, *PTERMINAL_SLAVE;
  90. /*++
  91. Structure Description:
  92. This structure defines an entry in the terminal history list.
  93. Members:
  94. ListEntry - Stores pointers to the next and previous history entries.
  95. CommandLength - Stores the length of the command, in bytes. There is no
  96. null terminator on this string.
  97. Command - Stores the command buffer.
  98. --*/
  99. typedef struct _TERMINAL_HISTORY_ENTRY {
  100. LIST_ENTRY ListEntry;
  101. ULONG CommandLength;
  102. CHAR Command[ANYSIZE_ARRAY];
  103. } TERMINAL_HISTORY_ENTRY, *PTERMINAL_HISTORY_ENTRY;
  104. /*++
  105. Structure Description:
  106. This structure defines terminal structure.
  107. Members:
  108. Header - Stores the standard object header.
  109. ListEntry - Stores pointers to the next and previous terminals in the
  110. global list.
  111. Number - Stores the terminal number.
  112. OutputBuffer - Stores the output buffer (buffer going out of the slave into
  113. the master).
  114. OutputBufferStart - Stores the first valid index of the output buffer.
  115. OutputBufferEnd - Stores the first invalid index of the output buffer. If
  116. this is equal to the start, then the buffer is empty.
  117. OutputLock - Stores a pointer to a lock serializing access to the output
  118. buffer.
  119. InputBuffer - Stores a pointer to the input buffer.
  120. InputBufferStart - Stores the first valid index of the input buffer.
  121. InputBufferEnd - Stores the first invalid index of the input buffer. If
  122. this is equal to the start, then the buffer is empty.
  123. WorkingInputBuffer - Stores the current (unfinished) line in canonical
  124. mode.
  125. WorkingInputCursor - Stores the current position of the cursor in the
  126. working input buffer.
  127. WorkingInputLength - Stores the valid length of the working input buffer.
  128. InputLock - Stores a pointer to a lock serializing access to the
  129. working input buffer.
  130. WorkingInputLock - Stores a pointer to the working input buffer lock.
  131. Settings - Stores the current terminal settings.
  132. CommandHistory - Stores the list of historical commands.
  133. CommandHistorySize - Stores the length of the linked list of commands.
  134. LastCommand - Stores a pointer to the most recent command.
  135. Flags - Stores a bitfield of flags. See TERMINAL_FLAG_* definitions. This
  136. field is protected by the terminal output lock.
  137. KeyData - Stores the data for the key currently being parsed. This is only
  138. used in canonical mode.
  139. SlaveHandles - Stores the count of open slave side handles, not counting
  140. those opened with no access.
  141. ProcessGroupId - Stores the owning process group ID of the terminal.
  142. SessionId - Stores the owning session ID of the terminal.
  143. SessionProcess - Stores a pointer to the session leader process for the
  144. owning session.
  145. ConnectionLock - Stores a spin lock that synchronizes the connection
  146. between the master and the slave, ensuring they both shut down in an
  147. orderly fashion.
  148. MasterReferenceCount - Stores the number of references on the master. This
  149. amounts to the number of open file handles plus one additional
  150. reference set on initialization.
  151. Slave - Stores a pointer to the corresponding slave object.
  152. SlaveFileObject - Stores a pointer to the slave's file object.
  153. MasterFileObject - Stores a pointer to the master's file object.
  154. WindowSize - Stores the window size of the terminal.
  155. ModemStatus - Stores the modem status bits.
  156. HardwareHandle - Stores an optional handle to the hardware device backing
  157. this terminal.
  158. SlavePathPoint - Stores the path point of the slave device, used to unlink
  159. it when the last master handle is closed.
  160. --*/
  161. typedef struct _TERMINAL {
  162. OBJECT_HEADER Header;
  163. LIST_ENTRY ListEntry;
  164. ULONG Number;
  165. PSTR OutputBuffer;
  166. ULONG OutputBufferStart;
  167. ULONG OutputBufferEnd;
  168. PQUEUED_LOCK OutputLock;
  169. PSTR InputBuffer;
  170. ULONG InputBufferStart;
  171. ULONG InputBufferEnd;
  172. PSTR WorkingInputBuffer;
  173. ULONG WorkingInputCursor;
  174. ULONG WorkingInputLength;
  175. PQUEUED_LOCK InputLock;
  176. PQUEUED_LOCK WorkingInputLock;
  177. TERMINAL_SETTINGS Settings;
  178. LIST_ENTRY CommandHistory;
  179. ULONG CommandHistorySize;
  180. PTERMINAL_HISTORY_ENTRY LastCommand;
  181. TERMINAL_KEY_DATA KeyData;
  182. ULONG Flags;
  183. UINTN SlaveHandles;
  184. PROCESS_GROUP_ID ProcessGroupId;
  185. SESSION_ID SessionId;
  186. PKPROCESS SessionProcess;
  187. ULONG MasterReferenceCount;
  188. PTERMINAL_SLAVE Slave;
  189. PFILE_OBJECT SlaveFileObject;
  190. PFILE_OBJECT MasterFileObject;
  191. TERMINAL_WINDOW_SIZE WindowSize;
  192. INT ModemStatus;
  193. PIO_HANDLE HardwareHandle;
  194. PATH_POINT SlavePathPoint;
  195. } TERMINAL, *PTERMINAL;
  196. /*++
  197. Structure Description:
  198. This structure defines the slave terminal structure.
  199. Members:
  200. Header - Stores the standard object header.
  201. Master - Stores a pointer to the master terminal.
  202. --*/
  203. struct _TERMINAL_SLAVE {
  204. OBJECT_HEADER Header;
  205. PTERMINAL Master;
  206. };
  207. /*++
  208. Structure Description:
  209. This structure defines the parameters sent during a creation request of
  210. a terminal object.
  211. Members:
  212. SlaveCreatePermissions - Store a pointer to the permissions used when
  213. creating the slave side.
  214. Master - Stores a pointer to the master terminal. When creating a master
  215. terminal, this parameter will be filled in during create. When creating
  216. a slave, this parameter must already be filled in and will be used.
  217. --*/
  218. typedef struct _TERMINAL_CREATION_PARAMETERS {
  219. FILE_PERMISSIONS SlaveCreatePermissions;
  220. PTERMINAL Master;
  221. } TERMINAL_CREATION_PARAMETERS, *PTERMINAL_CREATION_PARAMETERS;
  222. //
  223. // ----------------------------------------------- Internal Function Prototypes
  224. //
  225. KSTATUS
  226. IopCreateTerminalObject (
  227. FILE_PERMISSIONS CreatePermissions,
  228. PTERMINAL *NewTerminal
  229. );
  230. VOID
  231. IopDestroyTerminal (
  232. PVOID TerminalObject
  233. );
  234. KSTATUS
  235. IopTerminalMasterWrite (
  236. PFILE_OBJECT FileObject,
  237. PIO_CONTEXT IoContext
  238. );
  239. KSTATUS
  240. IopTerminalSlaveWrite (
  241. PFILE_OBJECT FileObject,
  242. PIO_CONTEXT IoContext
  243. );
  244. KSTATUS
  245. IopTerminalMasterRead (
  246. PFILE_OBJECT FileObject,
  247. PIO_CONTEXT IoContext
  248. );
  249. KSTATUS
  250. IopTerminalSlaveRead (
  251. PFILE_OBJECT FileObject,
  252. PIO_CONTEXT IoContext
  253. );
  254. KSTATUS
  255. IopTerminalWriteOutputBuffer (
  256. PTERMINAL Terminal,
  257. PVOID Buffer,
  258. UINTN SizeInBytes,
  259. ULONG RepeatCount,
  260. ULONG TimeoutInMilliseconds
  261. );
  262. ULONG
  263. IopTerminalGetInputBufferSpace (
  264. PTERMINAL Terminal
  265. );
  266. ULONG
  267. IopTerminalGetOutputBufferSpace (
  268. PTERMINAL Terminal
  269. );
  270. KSTATUS
  271. IopTerminalFixUpCanonicalLine (
  272. PTERMINAL Terminal,
  273. ULONG TimeoutInMilliseconds,
  274. ULONG DirtyRegionBegin,
  275. ULONG DirtyRegionEnd,
  276. ULONG CurrentScreenPosition
  277. );
  278. BOOL
  279. IopTerminalProcessEditingCharacter (
  280. PTERMINAL Terminal,
  281. CHAR Character,
  282. ULONG TimeoutInMilliseconds,
  283. PULONG DirtyRegionBegin,
  284. PULONG DirtyRegionEnd,
  285. PULONG ScreenCursorPosition,
  286. PBOOL OutputWritten
  287. );
  288. VOID
  289. IopTerminalAddHistoryEntry (
  290. PTERMINAL Terminal
  291. );
  292. KSTATUS
  293. IopTerminalUserBufferCopy (
  294. BOOL FromKernelMode,
  295. BOOL FromBuffer,
  296. PVOID UserBuffer,
  297. PVOID LocalBuffer,
  298. UINTN Size
  299. );
  300. KSTATUS
  301. IopTerminalFlushOutputToDevice (
  302. PTERMINAL Terminal
  303. );
  304. PTERMINAL
  305. IopLookupTerminal (
  306. SESSION_ID SessionId
  307. );
  308. VOID
  309. IopRelinquishTerminal (
  310. PTERMINAL Terminal
  311. );
  312. //
  313. // -------------------------------------------------------------------- Globals
  314. //
  315. //
  316. // Store a pointer to the global terminal directory.
  317. //
  318. PVOID IoTerminalDirectory;
  319. //
  320. // Store the global list of terminals.
  321. //
  322. LIST_ENTRY IoTerminalList;
  323. PQUEUED_LOCK IoTerminalListLock;
  324. //
  325. // Store a pointer to the local console terminal.
  326. //
  327. PIO_HANDLE IoLocalConsole;
  328. //
  329. // ------------------------------------------------------------------ Functions
  330. //
  331. KERNEL_API
  332. KSTATUS
  333. IoCreateTerminal (
  334. BOOL FromKernelMode,
  335. PIO_HANDLE MasterDirectory,
  336. PIO_HANDLE SlaveDirectory,
  337. PSTR MasterPath,
  338. UINTN MasterPathLength,
  339. PSTR SlavePath,
  340. UINTN SlavePathLength,
  341. ULONG MasterAccess,
  342. ULONG MasterOpenFlags,
  343. FILE_PERMISSIONS MasterCreatePermissions,
  344. FILE_PERMISSIONS SlaveCreatePermissions,
  345. PIO_HANDLE *MasterHandle
  346. )
  347. /*++
  348. Routine Description:
  349. This routine creates and opens a new terminal master.
  350. Arguments:
  351. FromKernelMode - Supplies a boolean indicating whether this request is
  352. originating from kernel mode (and should use the root path as a base)
  353. or user mode.
  354. MasterDirectory - Supplies an optional pointer to an open handle to a
  355. directory for relative paths when creating the master. Supply NULL to
  356. use the current working directory.
  357. SlaveDirectory - Supplies an optional pointer to an open handle to a
  358. directory for relative paths when creating the slave. Supply NULL to
  359. use the current working directory.
  360. MasterPath - Supplies an optional pointer to the path to create for the
  361. master.
  362. MasterPathLength - Supplies the length of the master side path buffer in
  363. bytes, including the null terminator.
  364. SlavePath - Supplies an optional pointer to the path to create for the
  365. master.
  366. SlavePathLength - Supplies the length of the slave side path buffer in
  367. bytes, including the null terminator.
  368. MasterAccess - Supplies the desired access permissions to the master side
  369. handle. See IO_ACCESS_* definitions.
  370. MasterOpenFlags - Supplies the open flags to use when opening the master.
  371. MasterCreatePermissions - Supplies the permissions to apply to the created
  372. master side.
  373. SlaveCreatePermissions - Supplies the permission to apply to the created
  374. slave side.
  375. MasterHandle - Supplies a pointer where a handle to the master side of the
  376. new terminal will be returned.
  377. Return Value:
  378. Status code.
  379. --*/
  380. {
  381. TERMINAL_CREATION_PARAMETERS CreationParameters;
  382. PIO_HANDLE SlaveHandle;
  383. KSTATUS Status;
  384. RtlZeroMemory(&CreationParameters, sizeof(TERMINAL_CREATION_PARAMETERS));
  385. CreationParameters.SlaveCreatePermissions = SlaveCreatePermissions;
  386. //
  387. // First try to open the master.
  388. //
  389. MasterOpenFlags |= OPEN_FLAG_CREATE | OPEN_FLAG_FAIL_IF_EXISTS;
  390. Status = IopOpen(FromKernelMode,
  391. MasterDirectory,
  392. MasterPath,
  393. MasterPathLength,
  394. MasterAccess,
  395. MasterOpenFlags,
  396. IoObjectTerminalMaster,
  397. &CreationParameters,
  398. MasterCreatePermissions,
  399. MasterHandle);
  400. if (!KSUCCESS(Status)) {
  401. return Status;
  402. }
  403. //
  404. // The master put itself in the creation parameters, which are now passed
  405. // down when trying to create the slave (which is mostly just a matter of
  406. // creating the path entry now).
  407. //
  408. MasterOpenFlags |= OPEN_FLAG_NO_CONTROLLING_TERMINAL;
  409. Status = IopOpen(FromKernelMode,
  410. SlaveDirectory,
  411. SlavePath,
  412. SlavePathLength,
  413. 0,
  414. MasterOpenFlags,
  415. IoObjectTerminalSlave,
  416. &CreationParameters,
  417. SlaveCreatePermissions,
  418. &SlaveHandle);
  419. if (!KSUCCESS(Status)) {
  420. IoClose(*MasterHandle);
  421. }
  422. //
  423. // Copy the path entry, then close the slave handle.
  424. //
  425. ASSERT((CreationParameters.Master != NULL) &&
  426. (CreationParameters.Master->SlavePathPoint.PathEntry == NULL));
  427. IO_COPY_PATH_POINT(&(CreationParameters.Master->SlavePathPoint),
  428. &(SlaveHandle->PathPoint));
  429. IO_PATH_POINT_ADD_REFERENCE(&(CreationParameters.Master->SlavePathPoint));
  430. IoClose(SlaveHandle);
  431. return Status;
  432. }
  433. KERNEL_API
  434. KSTATUS
  435. IoOpenLocalTerminalMaster (
  436. PIO_HANDLE *TerminalMaster
  437. )
  438. /*++
  439. Routine Description:
  440. This routine opens the master side of the local console terminal. This
  441. routine is intended to be used by the input and output devices that
  442. actually service the local console (the user input driver and video
  443. console driver).
  444. Arguments:
  445. TerminalMaster - Supplies a pointer where the I/O handle representing the
  446. master side of the local console terminal will be returned.
  447. Return Value:
  448. Status code.
  449. --*/
  450. {
  451. if (IoLocalConsole == NULL) {
  452. return STATUS_NOT_READY;
  453. }
  454. IoIoHandleAddReference(IoLocalConsole);
  455. *TerminalMaster = IoLocalConsole;
  456. return STATUS_SUCCESS;
  457. }
  458. KERNEL_API
  459. KSTATUS
  460. IoSetTerminalSettings (
  461. PIO_HANDLE TerminalHandle,
  462. PTERMINAL_SETTINGS NewSettings,
  463. PTERMINAL_SETTINGS OriginalSettings,
  464. TERMINAL_CHANGE_BEHAVIOR When
  465. )
  466. /*++
  467. Routine Description:
  468. This routine gets or sets the current terminal settings.
  469. Arguments:
  470. TerminalHandle - Supplies a pointer to the I/O handle of the terminal to
  471. change.
  472. NewSettings - Supplies an optional pointer to the new terminal settings.
  473. If this pointer is NULL, then the current settings will be retrieved
  474. but no changes will be made.
  475. OriginalSettings - Supplies an optional pointer where the current
  476. settings will be returned.
  477. When - Supplies when the new change should occur.
  478. Return Value:
  479. Status code.
  480. --*/
  481. {
  482. PFILE_OBJECT FileObject;
  483. KSTATUS Status;
  484. PTERMINAL Terminal;
  485. PTERMINAL_SLAVE TerminalSlave;
  486. //
  487. // Get a pointer to the actual terminal structure.
  488. //
  489. FileObject = TerminalHandle->PathPoint.PathEntry->FileObject;
  490. if (FileObject->Properties.Type == IoObjectTerminalMaster) {
  491. Terminal = FileObject->SpecialIo;
  492. } else if (FileObject->Properties.Type == IoObjectTerminalSlave) {
  493. TerminalSlave = FileObject->SpecialIo;
  494. Terminal = TerminalSlave->Master;
  495. } else {
  496. return STATUS_NOT_A_TERMINAL;
  497. }
  498. ASSERT(KeGetRunLevel() == RunLevelLow);
  499. //
  500. // Lock down the terminal for this. The order of lock acquisition is
  501. // important.
  502. //
  503. KeAcquireQueuedLock(Terminal->OutputLock);
  504. KeAcquireQueuedLock(Terminal->InputLock);
  505. if (OriginalSettings != NULL) {
  506. RtlCopyMemory(OriginalSettings,
  507. &(Terminal->Settings),
  508. sizeof(TERMINAL_SETTINGS));
  509. }
  510. if (NewSettings != NULL) {
  511. //
  512. // Fail if an unsupported feature was requested. Consider adding
  513. // support for said feature.
  514. //
  515. if (((NewSettings->InputFlags &
  516. TERMINAL_UNIMPLEMENTED_INPUT_FLAGS) != 0) ||
  517. ((NewSettings->OutputFlags &
  518. TERMINAL_UNIMPLEMENTED_OUTPUT_FLAGS) != 0) ||
  519. ((NewSettings->ControlFlags &
  520. TERMINAL_UNIMPLEMENTED_CONTROL_FLAGS) != 0)) {
  521. ASSERT(FALSE);
  522. Status = STATUS_NOT_SUPPORTED;
  523. goto SetTerminalSettingsEnd;
  524. }
  525. RtlCopyMemory(&(Terminal->Settings),
  526. NewSettings,
  527. sizeof(TERMINAL_SETTINGS));
  528. }
  529. //
  530. // If the user asked, remove all input.
  531. //
  532. if (When == TerminalChangeAfterOutputFlushInput) {
  533. Terminal->InputBufferStart = 0;
  534. Terminal->InputBufferEnd = 0;
  535. }
  536. Status = STATUS_SUCCESS;
  537. SetTerminalSettingsEnd:
  538. KeReleaseQueuedLock(Terminal->OutputLock);
  539. KeReleaseQueuedLock(Terminal->InputLock);
  540. return Status;
  541. }
  542. KERNEL_API
  543. KSTATUS
  544. IoTerminalSetDevice (
  545. PIO_HANDLE TerminalMaster,
  546. PIO_HANDLE DeviceHandle
  547. )
  548. /*++
  549. Routine Description:
  550. This routine associates or disassociates a terminal object with a device.
  551. Writes to the terminal slave will be forwarded to the associated terminal,
  552. as will changes to the terminal settings. If a device is being associated
  553. with the terminal, then the new settings will be sent to the device
  554. immediately in this routine.
  555. Arguments:
  556. TerminalMaster - Supplies a handle to the terminal master.
  557. DeviceHandle - Supplies a handle to the terminal hardware device. Any
  558. previously associated handle will be closed. Supply NULL to
  559. disassociate the terminal with a device. Upon success, this routine
  560. takes ownership of this device handle, and the caller should not close
  561. it manually.
  562. Return Value:
  563. Status code.
  564. --*/
  565. {
  566. PFILE_OBJECT FileObject;
  567. KSTATUS Status;
  568. PTERMINAL Terminal;
  569. FileObject = TerminalMaster->PathPoint.PathEntry->FileObject;
  570. if (FileObject->Properties.Type != IoObjectTerminalMaster) {
  571. Status = STATUS_NOT_A_TERMINAL;
  572. goto TerminalSetDeviceEnd;
  573. }
  574. Terminal = FileObject->SpecialIo;
  575. Status = STATUS_SUCCESS;
  576. //
  577. // Remove the old handle.
  578. //
  579. KeAcquireQueuedLock(Terminal->OutputLock);
  580. KeAcquireQueuedLock(Terminal->InputLock);
  581. if (Terminal->HardwareHandle != NULL) {
  582. IoClose(Terminal->HardwareHandle);
  583. }
  584. Terminal->HardwareHandle = DeviceHandle;
  585. //
  586. // If a new device is being associated with the terminal, send the settings
  587. // down to it now.
  588. //
  589. if (DeviceHandle != NULL) {
  590. Status = IoUserControl(DeviceHandle,
  591. TerminalControlSetAttributes,
  592. TRUE,
  593. &(Terminal->Settings),
  594. sizeof(TERMINAL_SETTINGS));
  595. }
  596. KeReleaseQueuedLock(Terminal->OutputLock);
  597. KeReleaseQueuedLock(Terminal->InputLock);
  598. TerminalSetDeviceEnd:
  599. return Status;
  600. }
  601. VOID
  602. IoRelinquishTerminal (
  603. PVOID Terminal,
  604. SESSION_ID SessionId,
  605. BOOL TerminalLocked
  606. )
  607. /*++
  608. Routine Description:
  609. This routine clears the controlling session and process group ID from
  610. the given terminal. It should only be called by process termination as
  611. a session leader is dying.
  612. Arguments:
  613. Terminal - Supplies a pointer to the controlling terminal.
  614. SessionId - Supplies the session ID of the session leader that's dying.
  615. TerminalLocked - Supplies a boolean indicating if the appropriate
  616. internal terminal locks are already held or not.
  617. Return Value:
  618. None.
  619. --*/
  620. {
  621. PTERMINAL TypedTerminal;
  622. TypedTerminal = Terminal;
  623. if (TerminalLocked == FALSE) {
  624. KeAcquireQueuedLock(TypedTerminal->OutputLock);
  625. KeAcquireQueuedLock(TypedTerminal->InputLock);
  626. }
  627. if (TypedTerminal->SessionId == SessionId) {
  628. IopRelinquishTerminal(TypedTerminal);
  629. }
  630. if (TerminalLocked == FALSE) {
  631. KeReleaseQueuedLock(TypedTerminal->OutputLock);
  632. KeReleaseQueuedLock(TypedTerminal->InputLock);
  633. }
  634. //
  635. // Release the reference acquired when the slave was opened and became
  636. // the process' controlling terminal.
  637. //
  638. ObReleaseReference(TypedTerminal);
  639. return;
  640. }
  641. KSTATUS
  642. IopInitializeTerminalSupport (
  643. VOID
  644. )
  645. /*++
  646. Routine Description:
  647. This routine is called during system initialization to set up support for
  648. terminals.
  649. Arguments:
  650. None.
  651. Return Value:
  652. Status code.
  653. --*/
  654. {
  655. KSTATUS Status;
  656. INITIALIZE_LIST_HEAD(&IoTerminalList);
  657. IoTerminalListLock = KeCreateQueuedLock();
  658. if (IoTerminalListLock == NULL) {
  659. Status = STATUS_INSUFFICIENT_RESOURCES;
  660. goto InitializeTerminalSupportEnd;
  661. }
  662. //
  663. // Create the Terminal object directory.
  664. //
  665. IoTerminalDirectory = ObCreateObject(ObjectDirectory,
  666. NULL,
  667. TERMINAL_DIRECTORY_NAME,
  668. sizeof(TERMINAL_DIRECTORY_NAME),
  669. sizeof(OBJECT_HEADER),
  670. NULL,
  671. OBJECT_FLAG_USE_NAME_DIRECTLY,
  672. TERMINAL_ALLOCATION_TAG);
  673. if (IoTerminalDirectory == NULL) {
  674. Status = STATUS_INSUFFICIENT_RESOURCES;
  675. goto InitializeTerminalSupportEnd;
  676. }
  677. //
  678. // Create a local console terminal.
  679. //
  680. Status = IoCreateTerminal(TRUE,
  681. NULL,
  682. NULL,
  683. NULL,
  684. 0,
  685. NULL,
  686. 0,
  687. IO_ACCESS_READ | IO_ACCESS_WRITE,
  688. 0,
  689. TERMINAL_DEFAULT_PERMISSIONS,
  690. TERMINAL_DEFAULT_PERMISSIONS,
  691. &IoLocalConsole);
  692. if (!KSUCCESS(Status)) {
  693. goto InitializeTerminalSupportEnd;
  694. }
  695. Status = STATUS_SUCCESS;
  696. InitializeTerminalSupportEnd:
  697. return Status;
  698. }
  699. KSTATUS
  700. IopTerminalOpenMaster (
  701. PIO_HANDLE IoHandle
  702. )
  703. /*++
  704. Routine Description:
  705. This routine is called when a master terminal was just opened.
  706. Arguments:
  707. IoHandle - Supplies a pointer to the I/O handle for the terminal.
  708. Return Value:
  709. Status code.
  710. --*/
  711. {
  712. PFILE_OBJECT FileObject;
  713. KSTATUS Status;
  714. PTERMINAL Terminal;
  715. FileObject = IoHandle->PathPoint.PathEntry->FileObject;
  716. ASSERT(FileObject->Properties.Type == IoObjectTerminalMaster);
  717. //
  718. // If no access is requested, then the special I/O terminal object does not
  719. // need to be present, and in fact, may not be.
  720. //
  721. if (IoHandle->Access == 0) {
  722. return STATUS_SUCCESS;
  723. }
  724. Terminal = FileObject->SpecialIo;
  725. if (Terminal == NULL) {
  726. Status = STATUS_NOT_READY;
  727. return Status;
  728. }
  729. ASSERT(Terminal->Header.Type == ObjectTerminalMaster);
  730. if (((Terminal->Flags & TERMINAL_FLAG_FAIL_OPENS) != 0) &&
  731. (!KSUCCESS(PsCheckPermission(PERMISSION_SYSTEM_ADMINISTRATOR)))) {
  732. return STATUS_RESOURCE_IN_USE;
  733. }
  734. //
  735. // Increment the number of parties that have the master terminal open. If
  736. // the initial reference taken on creation is gone, then this master
  737. // terminal is on its way out. That is, do not resurrect the master from 0
  738. // references.
  739. //
  740. Status = STATUS_SUCCESS;
  741. KeAcquireQueuedLock(IoTerminalListLock);
  742. if (Terminal->MasterReferenceCount == 0) {
  743. Status = STATUS_NO_SUCH_FILE;
  744. } else {
  745. Terminal->MasterReferenceCount += 1;
  746. }
  747. KeReleaseQueuedLock(IoTerminalListLock);
  748. return Status;
  749. }
  750. KSTATUS
  751. IopTerminalCloseMaster (
  752. PIO_HANDLE IoHandle
  753. )
  754. /*++
  755. Routine Description:
  756. This routine is called when a master terminal was just closed.
  757. Arguments:
  758. IoHandle - Supplies a pointer to the handle to close.
  759. Return Value:
  760. Status code.
  761. --*/
  762. {
  763. ULONG Events;
  764. PFILE_OBJECT FileObject;
  765. PTERMINAL Terminal;
  766. FileObject = IoHandle->PathPoint.PathEntry->FileObject;
  767. ASSERT(FileObject->Properties.Type == IoObjectTerminalMaster);
  768. //
  769. // Handles with no access never really counted and the special I/O object
  770. // may not have ever been present.
  771. //
  772. if (IoHandle->Access == 0) {
  773. return STATUS_SUCCESS;
  774. }
  775. Terminal = FileObject->SpecialIo;
  776. ASSERT(Terminal->Header.Type == ObjectTerminalMaster);
  777. //
  778. // Just decrement the reference count.
  779. //
  780. KeAcquireQueuedLock(IoTerminalListLock);
  781. ASSERT((Terminal->MasterReferenceCount > 1) &&
  782. (Terminal->MasterReferenceCount < 0x10000000));
  783. Terminal->MasterReferenceCount -= 1;
  784. //
  785. // If this was the last reference to match an open of the master, close the
  786. // connection between the master and the slave.
  787. //
  788. if (Terminal->MasterReferenceCount == 1) {
  789. //
  790. // Decrement the original reference, preventing any additional opens of
  791. // the master terminal during the destruction process. It is possible
  792. // that a path walk has already taken another reference on the master's
  793. // path entry.
  794. //
  795. Terminal->MasterReferenceCount -= 1;
  796. if (Terminal->SlaveFileObject != NULL) {
  797. Events = POLL_EVENT_IN | POLL_EVENT_OUT | POLL_EVENT_ERROR |
  798. POLL_EVENT_DISCONNECTED;
  799. IoSetIoObjectState(Terminal->SlaveFileObject->IoState,
  800. Events,
  801. TRUE);
  802. }
  803. //
  804. // Unlink the master.
  805. //
  806. IopDeleteByHandle(TRUE, IoHandle, 0);
  807. //
  808. // Unlink the slave.
  809. //
  810. if (Terminal->SlavePathPoint.PathEntry != NULL) {
  811. IopDeletePathPoint(TRUE, &(Terminal->SlavePathPoint), 0);
  812. IO_PATH_POINT_RELEASE_REFERENCE(&(Terminal->SlavePathPoint));
  813. Terminal->SlavePathPoint.PathEntry = NULL;
  814. }
  815. //
  816. // Release the initial reference on the slave file object taken when
  817. // the master was created.
  818. //
  819. if (Terminal->SlaveFileObject != NULL) {
  820. IopFileObjectReleaseReference(Terminal->SlaveFileObject);
  821. }
  822. }
  823. KeReleaseQueuedLock(IoTerminalListLock);
  824. return STATUS_SUCCESS;
  825. }
  826. KSTATUS
  827. IopTerminalOpenSlave (
  828. PIO_HANDLE IoHandle
  829. )
  830. /*++
  831. Routine Description:
  832. This routine opens the slave side of a terminal object.
  833. Arguments:
  834. IoHandle - Supplies a pointer to the I/O handle for the terminal.
  835. Return Value:
  836. Status code.
  837. --*/
  838. {
  839. PFILE_OBJECT FileObject;
  840. PKPROCESS Process;
  841. PROCESS_GROUP_ID ProcessGroup;
  842. SESSION_ID Session;
  843. PTERMINAL_SLAVE Slave;
  844. KSTATUS Status;
  845. PTERMINAL Terminal;
  846. BOOL TerminalLocksHeld;
  847. FileObject = IoHandle->PathPoint.PathEntry->FileObject;
  848. ASSERT(FileObject->Properties.Type == IoObjectTerminalSlave);
  849. //
  850. // If the caller doesn't actually want any access, then just let it slide.
  851. // The special I/O object may not be initialized.
  852. //
  853. if (IoHandle->Access == 0) {
  854. return STATUS_SUCCESS;
  855. }
  856. Slave = FileObject->SpecialIo;
  857. if (Slave == NULL) {
  858. return STATUS_NOT_READY;
  859. }
  860. ASSERT(Slave->Header.Type == ObjectTerminalSlave);
  861. TerminalLocksHeld = FALSE;
  862. KeAcquireQueuedLock(IoTerminalListLock);
  863. //
  864. // Get the master terminal. Synchronize this to avoid situations where the
  865. // master gets cleaned up after this pointer is read. Also synchronize with
  866. // the setting of the owning process group and session. Some of the user
  867. // controls synchronize terminal lookups with session ownership changes.
  868. //
  869. Terminal = Slave->Master;
  870. if (((Terminal->Flags & TERMINAL_FLAG_FAIL_OPENS) != 0) &&
  871. (!KSUCCESS(PsCheckPermission(PERMISSION_SYSTEM_ADMINISTRATOR)))) {
  872. Status = STATUS_RESOURCE_IN_USE;
  873. goto TerminalOpenSlaveEnd;
  874. }
  875. if (IO_IS_TERMINAL_MASTER_OPEN(Terminal) != FALSE) {
  876. ObAddReference(Terminal);
  877. IopFileObjectAddReference(Terminal->MasterFileObject);
  878. } else {
  879. Terminal = NULL;
  880. }
  881. if (Terminal == NULL) {
  882. Status = STATUS_TOO_LATE;
  883. goto TerminalOpenSlaveEnd;
  884. }
  885. //
  886. // Synchronize the check and set of the owning process group and session
  887. // with other opens and requests to change the process group and session.
  888. //
  889. TerminalLocksHeld = TRUE;
  890. KeAcquireQueuedLock(Terminal->OutputLock);
  891. KeAcquireQueuedLock(Terminal->InputLock);
  892. //
  893. // If the terminal is already open in another session, refuse to open.
  894. //
  895. Process = PsGetCurrentProcess();
  896. PsGetProcessGroup(Process, &ProcessGroup, &Session);
  897. if ((Terminal->SessionId != Session) &&
  898. (Terminal->SessionId != TERMINAL_INVALID_SESSION)) {
  899. Status = STATUS_RESOURCE_IN_USE;
  900. goto TerminalOpenSlaveEnd;
  901. }
  902. Terminal->SlaveHandles += 1;
  903. //
  904. // Clear the error that may have been set when the last previous slave
  905. // was closed.
  906. //
  907. if (Terminal->SlaveHandles == 1) {
  908. IoSetIoObjectState(Terminal->MasterFileObject->IoState,
  909. POLL_EVENT_DISCONNECTED,
  910. FALSE);
  911. }
  912. //
  913. // Make the terminal's owning session this one if it is a session leader
  914. // and doesn't already have a controlling terminal.
  915. //
  916. if ((IoHandle->OpenFlags & OPEN_FLAG_NO_CONTROLLING_TERMINAL) == 0) {
  917. if ((Terminal->SessionId == TERMINAL_INVALID_SESSION) &&
  918. (Process->Identifiers.ProcessId == Session)) {
  919. if (PsSetControllingTerminal(Process, Terminal) == NULL) {
  920. Terminal->ProcessGroupId = ProcessGroup;
  921. Terminal->SessionId = Session;
  922. Terminal->SessionProcess = Process;
  923. //
  924. // Add a reference that is released when the terminal is
  925. // relinquished.
  926. //
  927. ObAddReference(Terminal);
  928. }
  929. }
  930. }
  931. Status = STATUS_SUCCESS;
  932. TerminalOpenSlaveEnd:
  933. if (TerminalLocksHeld != FALSE) {
  934. KeReleaseQueuedLock(Terminal->InputLock);
  935. KeReleaseQueuedLock(Terminal->OutputLock);
  936. }
  937. KeReleaseQueuedLock(IoTerminalListLock);
  938. return Status;
  939. }
  940. KSTATUS
  941. IopTerminalCloseSlave (
  942. PIO_HANDLE IoHandle
  943. )
  944. /*++
  945. Routine Description:
  946. This routine is called when a master terminal was just closed.
  947. Arguments:
  948. IoHandle - Supplies a pointer to the handle to close.
  949. Return Value:
  950. Status code.
  951. --*/
  952. {
  953. PFILE_OBJECT FileObject;
  954. PTERMINAL PreviousTerminal;
  955. PTERMINAL_SLAVE Slave;
  956. PTERMINAL Terminal;
  957. FileObject = IoHandle->PathPoint.PathEntry->FileObject;
  958. ASSERT(FileObject->Properties.Type == IoObjectTerminalSlave);
  959. //
  960. // Handles with no access never really counted and the special I/O object
  961. // may not have been initialized.
  962. //
  963. if (IoHandle->Access == 0) {
  964. return STATUS_SUCCESS;
  965. }
  966. Slave = FileObject->SpecialIo;
  967. ASSERT(Slave->Header.Type == ObjectTerminalSlave);
  968. Terminal = Slave->Master;
  969. KeAcquireQueuedLock(Terminal->OutputLock);
  970. KeAcquireQueuedLock(Terminal->InputLock);
  971. ASSERT(Terminal->SlaveHandles != 0);
  972. Terminal->SlaveHandles -= 1;
  973. //
  974. // Tell the master no one's listening.
  975. //
  976. if (Terminal->SlaveHandles == 0) {
  977. IoSetIoObjectState(Terminal->MasterFileObject->IoState,
  978. POLL_EVENT_IN | POLL_EVENT_DISCONNECTED,
  979. TRUE);
  980. //
  981. // Also clear the controlling terminal. This may race with the process
  982. // dying. If this thread wins, it needs to release the reference.
  983. // Otherwise, it does not.
  984. //
  985. if (Terminal->SessionId != TERMINAL_INVALID_SESSION) {
  986. PreviousTerminal =
  987. PsSetControllingTerminal(Terminal->SessionProcess, NULL);
  988. if (PreviousTerminal != NULL) {
  989. ASSERT(PreviousTerminal == Terminal);
  990. IoRelinquishTerminal(Terminal, Terminal->SessionId, TRUE);
  991. } else {
  992. IopRelinquishTerminal(Terminal);
  993. }
  994. }
  995. }
  996. KeReleaseQueuedLock(Terminal->OutputLock);
  997. KeReleaseQueuedLock(Terminal->InputLock);
  998. //
  999. // Release the reference on the master taken during opening, which may
  1000. // allow the master to free itself.
  1001. //
  1002. ObReleaseReference(Slave->Master);
  1003. IopFileObjectReleaseReference(Slave->Master->MasterFileObject);
  1004. return STATUS_SUCCESS;
  1005. }
  1006. KSTATUS
  1007. IopPerformTerminalMasterIoOperation (
  1008. PIO_HANDLE Handle,
  1009. PIO_CONTEXT IoContext
  1010. )
  1011. /*++
  1012. Routine Description:
  1013. This routine reads from or writes to the master end of a terminal.
  1014. Arguments:
  1015. Handle - Supplies a pointer to the master terminal I/O handle.
  1016. IoContext - Supplies a pointer to the I/O context.
  1017. Return Value:
  1018. Status code. A failing status code does not necessarily mean no I/O made it
  1019. in or out. Check the bytes completed value in the I/O context to find out
  1020. how much occurred.
  1021. --*/
  1022. {
  1023. PFILE_OBJECT FileObject;
  1024. KSTATUS Status;
  1025. FileObject = Handle->PathPoint.PathEntry->FileObject;
  1026. ASSERT(IoContext->IoBuffer != NULL);
  1027. ASSERT(FileObject->Properties.Type == IoObjectTerminalMaster);
  1028. if (IoContext->Write != FALSE) {
  1029. Status = IopTerminalMasterWrite(FileObject, IoContext);
  1030. } else {
  1031. Status = IopTerminalMasterRead(FileObject, IoContext);
  1032. }
  1033. return Status;
  1034. }
  1035. KSTATUS
  1036. IopPerformTerminalSlaveIoOperation (
  1037. PIO_HANDLE Handle,
  1038. PIO_CONTEXT IoContext
  1039. )
  1040. /*++
  1041. Routine Description:
  1042. This routine reads from or writes to the slave end of a terminal.
  1043. Arguments:
  1044. Handle - Supplies a pointer to the slave terminal I/O handle.
  1045. IoContext - Supplies a pointer to the I/O context.
  1046. Return Value:
  1047. Status code. A failing status code does not necessarily mean no I/O made it
  1048. in or out. Check the bytes completed value in the I/O context to find out
  1049. how much occurred.
  1050. --*/
  1051. {
  1052. PFILE_OBJECT FileObject;
  1053. KSTATUS Status;
  1054. FileObject = Handle->PathPoint.PathEntry->FileObject;
  1055. ASSERT(IoContext->IoBuffer != NULL);
  1056. ASSERT(FileObject->Properties.Type == IoObjectTerminalSlave);
  1057. if (IoContext->Write != FALSE) {
  1058. Status = IopTerminalSlaveWrite(FileObject, IoContext);
  1059. } else {
  1060. Status = IopTerminalSlaveRead(FileObject, IoContext);
  1061. }
  1062. return Status;
  1063. }
  1064. KSTATUS
  1065. IopCreateTerminal (
  1066. IO_OBJECT_TYPE Type,
  1067. PVOID OverrideParameter,
  1068. FILE_PERMISSIONS CreatePermissions,
  1069. PFILE_OBJECT *FileObject
  1070. )
  1071. /*++
  1072. Routine Description:
  1073. This routine creates a terminal master or slave.
  1074. Arguments:
  1075. Type - Supplies the type of special object to create.
  1076. OverrideParameter - Supplies an optional parameter to send along with the
  1077. override type.
  1078. CreatePermissions - Supplies the permissions to assign to the new file.
  1079. FileObject - Supplies a pointer where a pointer to the new file object
  1080. will be returned on success.
  1081. Return Value:
  1082. Status code.
  1083. --*/
  1084. {
  1085. BOOL Created;
  1086. PTERMINAL_CREATION_PARAMETERS CreationParameters;
  1087. PLIST_ENTRY CurrentEntry;
  1088. BOOL ListLockHeld;
  1089. CHAR Name[TERMINAL_MAX_NAME_LENGTH];
  1090. ULONG NameLength;
  1091. PFILE_OBJECT NewFileObject;
  1092. ULONG Number;
  1093. FILE_PROPERTIES Properties;
  1094. KSTATUS Status;
  1095. PTERMINAL Terminal;
  1096. PTERMINAL TerminalAfter;
  1097. PLIST_ENTRY TerminalAfterEntry;
  1098. CreationParameters = OverrideParameter;
  1099. ListLockHeld = FALSE;
  1100. //
  1101. // If the object came up from out of the file system, don't actually
  1102. // create anything. The common case here is querying file properties.
  1103. //
  1104. if (CreationParameters == NULL) {
  1105. ASSERT(*FileObject != NULL);
  1106. Status = STATUS_SUCCESS;
  1107. goto CreateTerminalEnd;
  1108. }
  1109. NewFileObject = NULL;
  1110. //
  1111. // Create the slave file object.
  1112. //
  1113. if (Type == IoObjectTerminalSlave) {
  1114. ASSERT(CreationParameters->Master != NULL);
  1115. Terminal = CreationParameters->Master;
  1116. ASSERT(Terminal->SlaveFileObject == NULL);
  1117. //
  1118. // Create a new file object if there isn't one already.
  1119. //
  1120. if (*FileObject == NULL) {
  1121. IopFillOutFilePropertiesForObject(&Properties,
  1122. &(Terminal->Slave->Header));
  1123. Properties.Type = IoObjectTerminalSlave;
  1124. Properties.Permissions = CreatePermissions;
  1125. Status = IopCreateOrLookupFileObject(&Properties,
  1126. ObGetRootObject(),
  1127. 0,
  1128. &NewFileObject,
  1129. &Created);
  1130. if (!KSUCCESS(Status)) {
  1131. //
  1132. // Release the reference from when the properties were filled
  1133. // out above.
  1134. //
  1135. ObReleaseReference(Terminal->Slave);
  1136. goto CreateTerminalEnd;
  1137. }
  1138. ASSERT(Created != FALSE);
  1139. *FileObject = NewFileObject;
  1140. //
  1141. // With the file object created, but not yet ready, go ahead and
  1142. // name the terminal slave object. Once it has a name it can be
  1143. // found by other threads via path lookup, but those threads will
  1144. // have to wait on the file object's ready event before proceeding.
  1145. //
  1146. ASSERT(Terminal->Number != MAX_ULONG);
  1147. //
  1148. // Create the terminal name string (on the stack, it gets copied by
  1149. // the object manager) and then set the name in the object.
  1150. //
  1151. NameLength = RtlPrintToString(Name,
  1152. TERMINAL_MAX_NAME_LENGTH,
  1153. CharacterEncodingDefault,
  1154. TERMINAL_SLAVE_NAME_FORMAT,
  1155. Terminal->Number);
  1156. Status = ObNameObject(Terminal->Slave,
  1157. Name,
  1158. NameLength,
  1159. TERMINAL_ALLOCATION_TAG,
  1160. FALSE);
  1161. if (!KSUCCESS(Status)) {
  1162. ASSERT(Status != STATUS_TOO_LATE);
  1163. goto CreateTerminalEnd;
  1164. }
  1165. }
  1166. //
  1167. // Add a reference since the master holds a reference to the slave
  1168. // file object.
  1169. //
  1170. IopFileObjectAddReference(*FileObject);
  1171. //
  1172. // By setting the slave file object to non-null, this code is
  1173. // transferring the reference originally held by the master when the
  1174. // slave was created over to the file object special I/O field.
  1175. //
  1176. Terminal->SlaveFileObject = *FileObject;
  1177. ASSERT((*FileObject)->SpecialIo == NULL);
  1178. (*FileObject)->SpecialIo = Terminal->Slave;
  1179. //
  1180. // Create a master, which creates the slave object as well.
  1181. //
  1182. } else {
  1183. ASSERT(Type == IoObjectTerminalMaster);
  1184. ASSERT(CreationParameters->Master == NULL);
  1185. Terminal = NULL;
  1186. //
  1187. // Create the terminal object. This reference will get transferred to
  1188. // the file object special I/O field on success.
  1189. //
  1190. Status = IopCreateTerminalObject(
  1191. CreationParameters->SlaveCreatePermissions,
  1192. &Terminal);
  1193. if (!KSUCCESS(Status)) {
  1194. goto CreateTerminalEnd;
  1195. }
  1196. //
  1197. // Create a file object if necessary. This adds a reference on the
  1198. // object.
  1199. //
  1200. if (*FileObject == NULL) {
  1201. IopFillOutFilePropertiesForObject(&Properties, &(Terminal->Header));
  1202. Properties.Type = IoObjectTerminalMaster;
  1203. Properties.Permissions = CreatePermissions;
  1204. Status = IopCreateOrLookupFileObject(&Properties,
  1205. ObGetRootObject(),
  1206. 0,
  1207. &NewFileObject,
  1208. &Created);
  1209. if (!KSUCCESS(Status)) {
  1210. //
  1211. // Release both the references taken by creating the object and
  1212. // filling out the file properties.
  1213. //
  1214. ObReleaseReference(Terminal);
  1215. ObReleaseReference(Terminal);
  1216. goto CreateTerminalEnd;
  1217. }
  1218. ASSERT(Created != FALSE);
  1219. *FileObject = NewFileObject;
  1220. //
  1221. // With the file object created, but not yet ready, go ahead and
  1222. // name the terminal master object. Once it has a name it can be
  1223. // found by other threads via path lookup, but those threads will
  1224. // have to wait on the file object's ready event before proceeding.
  1225. //
  1226. Number = 0;
  1227. ListLockHeld = TRUE;
  1228. KeAcquireQueuedLock(IoTerminalListLock);
  1229. LIST_REMOVE(&(Terminal->ListEntry));
  1230. CurrentEntry = IoTerminalList.Next;
  1231. TerminalAfterEntry = &IoTerminalList;
  1232. while (CurrentEntry != &IoTerminalList) {
  1233. TerminalAfter = LIST_VALUE(CurrentEntry, TERMINAL, ListEntry);
  1234. //
  1235. // Assert that the list is in order.
  1236. //
  1237. ASSERT(TerminalAfter->Number >= Number);
  1238. if (TerminalAfter->Number == Number) {
  1239. Number += 1;
  1240. } else {
  1241. TerminalAfterEntry = CurrentEntry;
  1242. break;
  1243. }
  1244. CurrentEntry = CurrentEntry->Next;
  1245. }
  1246. //
  1247. // Create the terminal name string (on the stack, it gets copied by
  1248. // the object manager) and then set the name in the object.
  1249. //
  1250. NameLength = RtlPrintToString(Name,
  1251. TERMINAL_MAX_NAME_LENGTH,
  1252. CharacterEncodingDefault,
  1253. TERMINAL_MASTER_NAME_FORMAT,
  1254. Number);
  1255. Status = ObNameObject(Terminal,
  1256. Name,
  1257. NameLength,
  1258. TERMINAL_ALLOCATION_TAG,
  1259. FALSE);
  1260. if (!KSUCCESS(Status)) {
  1261. ASSERT(Status != STATUS_TOO_LATE);
  1262. goto CreateTerminalEnd;
  1263. }
  1264. ASSERT(Terminal->Number == MAX_ULONG);
  1265. Terminal->Number = Number;
  1266. INSERT_BEFORE(&(Terminal->ListEntry), TerminalAfterEntry);
  1267. KeReleaseQueuedLock(IoTerminalListLock);
  1268. ListLockHeld = FALSE;
  1269. }
  1270. ASSERT((*FileObject)->Properties.Type == IoObjectTerminalMaster);
  1271. Terminal->MasterFileObject = *FileObject;
  1272. CreationParameters->Master = Terminal;
  1273. ASSERT((*FileObject)->SpecialIo == NULL);
  1274. (*FileObject)->SpecialIo = Terminal;
  1275. }
  1276. Status = STATUS_SUCCESS;
  1277. CreateTerminalEnd:
  1278. if (ListLockHeld != FALSE) {
  1279. KeReleaseQueuedLock(IoTerminalListLock);
  1280. }
  1281. //
  1282. // On both success and failure, the file object's ready event needs to be
  1283. // signaled. Other threads may be waiting on the event.
  1284. //
  1285. if (*FileObject != NULL) {
  1286. ASSERT((KeGetEventState((*FileObject)->ReadyEvent) == NotSignaled) ||
  1287. (KeGetEventState((*FileObject)->ReadyEvent) ==
  1288. NotSignaledWithWaiters));
  1289. KeSignalEvent((*FileObject)->ReadyEvent, SignalOptionSignalAll);
  1290. }
  1291. return Status;
  1292. }
  1293. KSTATUS
  1294. IopUnlinkTerminal (
  1295. PFILE_OBJECT FileObject,
  1296. PBOOL Unlinked
  1297. )
  1298. /*++
  1299. Routine Description:
  1300. This routine unlinks a terminal from the accessible namespace.
  1301. Arguments:
  1302. FileObject - Supplies a pointer to the terminal's file object.
  1303. Unlinked - Supplies a pointer to a boolean that receives whether or not the
  1304. terminal was successfully unlinked.
  1305. Return Value:
  1306. Status code.
  1307. --*/
  1308. {
  1309. KSTATUS Status;
  1310. POBJECT_HEADER Terminal;
  1311. ASSERT((FileObject->Properties.Type == IoObjectTerminalMaster) ||
  1312. (FileObject->Properties.Type == IoObjectTerminalSlave));
  1313. ASSERT(KeIsSharedExclusiveLockHeldExclusive(FileObject->Lock) != FALSE);
  1314. Terminal = FileObject->SpecialIo;
  1315. ASSERT(Terminal != NULL);
  1316. *Unlinked = FALSE;
  1317. Status = ObUnlinkObject(Terminal);
  1318. if (KSUCCESS(Status)) {
  1319. *Unlinked = TRUE;
  1320. }
  1321. return Status;
  1322. }
  1323. KSTATUS
  1324. IopTerminalUserControl (
  1325. PIO_HANDLE Handle,
  1326. TERMINAL_USER_CONTROL_CODE CodeNumber,
  1327. BOOL FromKernelMode,
  1328. PVOID ContextBuffer,
  1329. UINTN ContextBufferSize
  1330. )
  1331. /*++
  1332. Routine Description:
  1333. This routine handles user control requests destined for a terminal object.
  1334. Arguments:
  1335. Handle - Supplies the open file handle.
  1336. CodeNumber - Supplies the minor code of the request.
  1337. FromKernelMode - Supplies a boolean indicating whether or not this request
  1338. (and the buffer associated with it) originates from user mode (FALSE)
  1339. or kernel mode (TRUE).
  1340. ContextBuffer - Supplies a pointer to the context buffer allocated by the
  1341. caller for the request.
  1342. ContextBufferSize - Supplies the size of the supplied context buffer.
  1343. Return Value:
  1344. Status code.
  1345. --*/
  1346. {
  1347. BOOL AcceptingSignal;
  1348. INT Argument;
  1349. IO_CONTEXT Context;
  1350. PTERMINAL ControllingTerminal;
  1351. PROCESS_GROUP_ID CurrentProcessGroupId;
  1352. SESSION_ID CurrentSessionId;
  1353. PFILE_OBJECT FileObject;
  1354. KSTATUS HardwareStatus;
  1355. UINTN Index;
  1356. BOOL InSession;
  1357. IO_BUFFER IoBuffer;
  1358. ULONG IoBufferFlags;
  1359. INT ModemStatus;
  1360. TERMINAL_SETTINGS_OLD OldSettings;
  1361. PTERMINAL PreviousTerminal;
  1362. PKPROCESS Process;
  1363. PROCESS_GROUP_ID ProcessGroupId;
  1364. INT QueueSize;
  1365. SESSION_ID SessionId;
  1366. TERMINAL_SETTINGS Settings;
  1367. KSTATUS Status;
  1368. PTERMINAL Terminal;
  1369. PTERMINAL_SLAVE TerminalSlave;
  1370. TERMINAL_CHANGE_BEHAVIOR When;
  1371. TERMINAL_WINDOW_SIZE WindowSize;
  1372. TerminalSlave = NULL;
  1373. FileObject = Handle->PathPoint.PathEntry->FileObject;
  1374. if (FileObject->Properties.Type == IoObjectTerminalMaster) {
  1375. Terminal = FileObject->SpecialIo;
  1376. } else if (FileObject->Properties.Type == IoObjectTerminalSlave) {
  1377. TerminalSlave = FileObject->SpecialIo;
  1378. Terminal = TerminalSlave->Master;
  1379. } else {
  1380. return STATUS_NOT_A_TERMINAL;
  1381. }
  1382. switch (CodeNumber) {
  1383. case TerminalControlGetAttributes:
  1384. Status = IoSetTerminalSettings(Handle,
  1385. NULL,
  1386. &Settings,
  1387. TerminalChangeNone);
  1388. if (!KSUCCESS(Status)) {
  1389. break;
  1390. }
  1391. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1392. FALSE,
  1393. ContextBuffer,
  1394. &Settings,
  1395. sizeof(TERMINAL_SETTINGS));
  1396. break;
  1397. case TerminalControlSetAttributes:
  1398. case TerminalControlSetAttributesDrain:
  1399. case TerminalControlSetAttributesFlush:
  1400. if (CodeNumber == TerminalControlSetAttributes) {
  1401. When = TerminalChangeNow;
  1402. } else if (CodeNumber == TerminalControlSetAttributesDrain) {
  1403. When = TerminalChangeAfterOutput;
  1404. } else {
  1405. ASSERT(CodeNumber == TerminalControlSetAttributesFlush);
  1406. When = TerminalChangeAfterOutputFlushInput;
  1407. }
  1408. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1409. TRUE,
  1410. ContextBuffer,
  1411. &Settings,
  1412. sizeof(TERMINAL_SETTINGS));
  1413. if (!KSUCCESS(Status)) {
  1414. break;
  1415. }
  1416. Status = IoSetTerminalSettings(Handle, &Settings, NULL, When);
  1417. break;
  1418. case TerminalControlGetAttributesOld:
  1419. Status = IoSetTerminalSettings(Handle,
  1420. NULL,
  1421. &Settings,
  1422. TerminalChangeNone);
  1423. if (!KSUCCESS(Status)) {
  1424. break;
  1425. }
  1426. RtlZeroMemory(&OldSettings, sizeof(TERMINAL_SETTINGS_OLD));
  1427. OldSettings.InputFlags = Settings.InputFlags;
  1428. OldSettings.OutputFlags = Settings.OutputFlags;
  1429. OldSettings.ControlFlags = Settings.ControlFlags;
  1430. OldSettings.LocalFlags = Settings.LocalFlags;
  1431. OldSettings.LineDiscipline = 0;
  1432. for (Index = 0;
  1433. Index < TERMINAL_SETTINGS_OLD_CONTROL_COUNT;
  1434. Index += 1) {
  1435. OldSettings.ControlCharacters[Index] =
  1436. Settings.ControlCharacters[Index];
  1437. }
  1438. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1439. FALSE,
  1440. ContextBuffer,
  1441. &OldSettings,
  1442. sizeof(TERMINAL_SETTINGS_OLD));
  1443. break;
  1444. case TerminalControlSetAttributesOld:
  1445. case TerminalControlSetAttributesDrainOld:
  1446. case TerminalControlSetAttributesFlushOld:
  1447. if (CodeNumber == TerminalControlSetAttributesOld) {
  1448. When = TerminalChangeNow;
  1449. } else if (CodeNumber == TerminalControlSetAttributesDrainOld) {
  1450. When = TerminalChangeAfterOutput;
  1451. } else {
  1452. ASSERT(CodeNumber == TerminalControlSetAttributesFlushOld);
  1453. When = TerminalChangeAfterOutputFlushInput;
  1454. }
  1455. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1456. TRUE,
  1457. ContextBuffer,
  1458. &OldSettings,
  1459. sizeof(TERMINAL_SETTINGS_OLD));
  1460. if (!KSUCCESS(Status)) {
  1461. break;
  1462. }
  1463. //
  1464. // Get the current settings, and copy the old to the new.
  1465. //
  1466. Status = IoSetTerminalSettings(Handle,
  1467. NULL,
  1468. &Settings,
  1469. TerminalChangeNone);
  1470. if (!KSUCCESS(Status)) {
  1471. break;
  1472. }
  1473. Settings.InputFlags = OldSettings.InputFlags;
  1474. Settings.OutputFlags = OldSettings.OutputFlags;
  1475. Settings.ControlFlags = OldSettings.ControlFlags;
  1476. Settings.LocalFlags = OldSettings.LocalFlags;
  1477. for (Index = 0;
  1478. Index < TERMINAL_SETTINGS_OLD_CONTROL_COUNT;
  1479. Index += 1) {
  1480. Settings.ControlCharacters[Index] =
  1481. OldSettings.ControlCharacters[Index];
  1482. }
  1483. //
  1484. // Set the new settings.
  1485. //
  1486. Status = IoSetTerminalSettings(Handle, &Settings, NULL, When);
  1487. break;
  1488. case TerminalControlSendBreak:
  1489. //
  1490. // The integer argument is the pointer itself.
  1491. //
  1492. Argument = (UINTN)ContextBuffer;
  1493. if (Argument == 0) {
  1494. Status = STATUS_SUCCESS;
  1495. } else {
  1496. //
  1497. // A non-zero argument is undefined. Act like "drain" here, and
  1498. // wait for all output to complete.
  1499. //
  1500. Status = IopTerminalFlush(FileObject, FLUSH_FLAG_WRITE);
  1501. }
  1502. break;
  1503. case TerminalControlFlowControl:
  1504. Status = STATUS_SUCCESS;
  1505. break;
  1506. case TerminalControlFlush:
  1507. //
  1508. // The argument is an integer.
  1509. //
  1510. Argument = (UINTN)ContextBuffer;
  1511. Argument &= FLUSH_FLAG_READ | FLUSH_FLAG_WRITE;
  1512. Argument |= FLUSH_FLAG_DISCARD;
  1513. Status = IopTerminalFlush(FileObject, Argument);
  1514. break;
  1515. case TerminalControlSetExclusive:
  1516. case TerminalControlClearExclusive:
  1517. KeAcquireQueuedLock(Terminal->OutputLock);
  1518. KeAcquireQueuedLock(Terminal->InputLock);
  1519. if (CodeNumber == TerminalControlSetExclusive) {
  1520. Terminal->Flags |= TERMINAL_FLAG_FAIL_OPENS;
  1521. } else {
  1522. Terminal->Flags &= ~TERMINAL_FLAG_FAIL_OPENS;
  1523. }
  1524. KeReleaseQueuedLock(Terminal->InputLock);
  1525. KeReleaseQueuedLock(Terminal->OutputLock);
  1526. Status = STATUS_SUCCESS;
  1527. break;
  1528. case TerminalControlGetOutputQueueSize:
  1529. case TerminalControlGetInputQueueSize:
  1530. KeAcquireQueuedLock(Terminal->OutputLock);
  1531. KeAcquireQueuedLock(Terminal->InputLock);
  1532. if (CodeNumber == TerminalControlGetOutputQueueSize) {
  1533. QueueSize = IopTerminalGetOutputBufferSpace(Terminal) -
  1534. TERMINAL_OUTPUT_BUFFER_SIZE;
  1535. } else {
  1536. QueueSize = IopTerminalGetInputBufferSpace(Terminal) -
  1537. TERMINAL_INPUT_BUFFER_SIZE;
  1538. }
  1539. KeReleaseQueuedLock(Terminal->InputLock);
  1540. KeReleaseQueuedLock(Terminal->OutputLock);
  1541. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1542. FALSE,
  1543. ContextBuffer,
  1544. &QueueSize,
  1545. sizeof(INT));
  1546. break;
  1547. case TerminalControlInsertInInputQueue:
  1548. IoBufferFlags = 0;
  1549. if (FromKernelMode != FALSE) {
  1550. IoBufferFlags |= IO_BUFFER_FLAG_KERNEL_MODE_DATA;
  1551. }
  1552. Status = MmInitializeIoBuffer(&IoBuffer,
  1553. ContextBuffer,
  1554. INVALID_PHYSICAL_ADDRESS,
  1555. 1,
  1556. IoBufferFlags);
  1557. if (!KSUCCESS(Status)) {
  1558. break;
  1559. }
  1560. Context.IoBuffer = &IoBuffer;
  1561. Context.SizeInBytes = 1;
  1562. Context.Flags = 0;
  1563. Context.TimeoutInMilliseconds = WAIT_TIME_INDEFINITE;
  1564. Status = IopTerminalMasterWrite(Terminal->MasterFileObject, &Context);
  1565. break;
  1566. case TerminalControlGetWindowSize:
  1567. KeAcquireQueuedLock(Terminal->OutputLock);
  1568. RtlCopyMemory(&WindowSize,
  1569. &(Terminal->WindowSize),
  1570. sizeof(TERMINAL_WINDOW_SIZE));
  1571. KeReleaseQueuedLock(Terminal->OutputLock);
  1572. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1573. FALSE,
  1574. ContextBuffer,
  1575. &WindowSize,
  1576. sizeof(TERMINAL_WINDOW_SIZE));
  1577. break;
  1578. case TerminalControlSetWindowSize:
  1579. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1580. TRUE,
  1581. ContextBuffer,
  1582. &WindowSize,
  1583. sizeof(TERMINAL_WINDOW_SIZE));
  1584. if (!KSUCCESS(Status)) {
  1585. break;
  1586. }
  1587. KeAcquireQueuedLock(Terminal->OutputLock);
  1588. RtlCopyMemory(&(Terminal->WindowSize),
  1589. &WindowSize,
  1590. sizeof(TERMINAL_WINDOW_SIZE));
  1591. KeReleaseQueuedLock(Terminal->OutputLock);
  1592. break;
  1593. case TerminalControlGetModemStatus:
  1594. case TerminalControlOrModemStatus:
  1595. case TerminalControlClearModemStatus:
  1596. case TerminalControlSetModemStatus:
  1597. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1598. TRUE,
  1599. ContextBuffer,
  1600. &ModemStatus,
  1601. sizeof(INT));
  1602. if (!KSUCCESS(Status)) {
  1603. break;
  1604. }
  1605. KeAcquireQueuedLock(Terminal->OutputLock);
  1606. if (CodeNumber == TerminalControlOrModemStatus) {
  1607. Terminal->ModemStatus |= ModemStatus;
  1608. } else if (CodeNumber == TerminalControlClearModemStatus) {
  1609. Terminal->ModemStatus &= ~ModemStatus;
  1610. } else if (CodeNumber == TerminalControlSetModemStatus) {
  1611. Terminal->ModemStatus = ModemStatus;
  1612. }
  1613. ModemStatus = Terminal->ModemStatus;
  1614. KeReleaseQueuedLock(Terminal->OutputLock);
  1615. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1616. FALSE,
  1617. ContextBuffer,
  1618. &ModemStatus,
  1619. sizeof(INT));
  1620. break;
  1621. case TerminalControlGetSoftCarrier:
  1622. case TerminalControlSetSoftCarrier:
  1623. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1624. TRUE,
  1625. ContextBuffer,
  1626. &Argument,
  1627. sizeof(INT));
  1628. if (!KSUCCESS(Status)) {
  1629. break;
  1630. }
  1631. KeAcquireQueuedLock(Terminal->OutputLock);
  1632. KeAcquireQueuedLock(Terminal->InputLock);
  1633. if (CodeNumber == TerminalControlSetSoftCarrier) {
  1634. Terminal->ModemStatus |= ModemStatus;
  1635. if (Argument != 0) {
  1636. Terminal->Settings.ControlFlags |= TERMINAL_CONTROL_NO_HANGUP;
  1637. } else {
  1638. Terminal->Settings.ControlFlags &= ~TERMINAL_CONTROL_NO_HANGUP;
  1639. }
  1640. }
  1641. Argument = FALSE;
  1642. if ((Terminal->Settings.ControlFlags & TERMINAL_CONTROL_NO_HANGUP) !=
  1643. 0) {
  1644. Argument = TRUE;
  1645. }
  1646. KeReleaseQueuedLock(Terminal->InputLock);
  1647. KeReleaseQueuedLock(Terminal->OutputLock);
  1648. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1649. FALSE,
  1650. ContextBuffer,
  1651. &Argument,
  1652. sizeof(INT));
  1653. break;
  1654. case TerminalControlGetProcessGroup:
  1655. //
  1656. // The given terminal must be the controlling terminal of the calling
  1657. // process.
  1658. //
  1659. PsGetProcessGroup(NULL, &CurrentProcessGroupId, &CurrentSessionId);
  1660. Status = STATUS_SUCCESS;
  1661. KeAcquireQueuedLock(Terminal->OutputLock);
  1662. KeAcquireQueuedLock(Terminal->InputLock);
  1663. if (Terminal->SessionId != CurrentSessionId) {
  1664. Status = STATUS_NOT_A_TERMINAL;
  1665. } else {
  1666. ProcessGroupId = Terminal->ProcessGroupId;
  1667. }
  1668. KeReleaseQueuedLock(Terminal->InputLock);
  1669. KeReleaseQueuedLock(Terminal->OutputLock);
  1670. if (!KSUCCESS(Status)) {
  1671. break;
  1672. }
  1673. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1674. FALSE,
  1675. ContextBuffer,
  1676. &ProcessGroupId,
  1677. sizeof(PROCESS_GROUP_ID));
  1678. break;
  1679. case TerminalControlSetProcessGroup:
  1680. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1681. TRUE,
  1682. ContextBuffer,
  1683. &ProcessGroupId,
  1684. sizeof(PROCESS_GROUP_ID));
  1685. if (!KSUCCESS(Status)) {
  1686. break;
  1687. }
  1688. //
  1689. // If the terminal does not have the slave side attached or does not
  1690. // belong to the calling session, then the process does not have
  1691. // permission to update its process group.
  1692. //
  1693. PsGetProcessGroup(NULL, &CurrentProcessGroupId, &CurrentSessionId);
  1694. //
  1695. // The given terminal must be in the current session.
  1696. //
  1697. InSession = PsIsProcessGroupInSession(ProcessGroupId, CurrentSessionId);
  1698. if (InSession == FALSE) {
  1699. Status = STATUS_PERMISSION_DENIED;
  1700. break;
  1701. }
  1702. AcceptingSignal = FALSE;
  1703. KeAcquireQueuedLock(Terminal->OutputLock);
  1704. KeAcquireQueuedLock(Terminal->InputLock);
  1705. if (Terminal->SessionId != CurrentSessionId) {
  1706. Status = STATUS_NOT_A_TERMINAL;
  1707. //
  1708. // If the calling process is not in the owning (foreground) process
  1709. // group, then it is sent a signal unless it is blocking or ignoring
  1710. // the background terminal output signal.
  1711. //
  1712. } else {
  1713. if (CurrentProcessGroupId != Terminal->ProcessGroupId) {
  1714. AcceptingSignal = PsIsThreadAcceptingSignal(
  1715. NULL,
  1716. SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
  1717. }
  1718. //
  1719. // If the process is not accepting signals or the signal did not
  1720. // need to be checked, then the process group is free to change.
  1721. //
  1722. if (AcceptingSignal == FALSE) {
  1723. Terminal->ProcessGroupId = ProcessGroupId;
  1724. Status = STATUS_SUCCESS;
  1725. }
  1726. }
  1727. KeReleaseQueuedLock(Terminal->InputLock);
  1728. KeReleaseQueuedLock(Terminal->OutputLock);
  1729. //
  1730. // If the process is accepting the signal checked above, send it and
  1731. // tell the caller to try again later. If it's not accepting it, just
  1732. // let it go through.
  1733. //
  1734. if (AcceptingSignal != FALSE) {
  1735. PsSignalProcessGroup(CurrentProcessGroupId,
  1736. SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
  1737. Status = STATUS_TRY_AGAIN;
  1738. }
  1739. break;
  1740. case TerminalControlSetControllingTerminal:
  1741. Argument = (UINTN)ContextBuffer;
  1742. //
  1743. // The calling process must be a session leader to set a controlling
  1744. // terminal, and must not have a controlling terminal already.
  1745. //
  1746. Process = PsGetCurrentProcess();
  1747. PsGetProcessGroup(Process, &CurrentProcessGroupId, &CurrentSessionId);
  1748. if (CurrentProcessGroupId != CurrentSessionId) {
  1749. Status = STATUS_PERMISSION_DENIED;
  1750. break;
  1751. }
  1752. //
  1753. // If the terminal already belongs to a different session, then it
  1754. // cannot bet set as the controlling terminal of this session unless
  1755. // the caller is root and the argument is 1.
  1756. //
  1757. if (Terminal->SessionId != TERMINAL_INVALID_SESSION) {
  1758. if (Terminal->SessionId == CurrentSessionId) {
  1759. Status = STATUS_SUCCESS;
  1760. break;
  1761. }
  1762. //
  1763. // Allow root to steal terminals from different session if the
  1764. // argument is non-zero.
  1765. //
  1766. Status = PsCheckPermission(PERMISSION_SYSTEM_ADMINISTRATOR);
  1767. if ((!KSUCCESS(Status)) || (Argument == 0)) {
  1768. Status = STATUS_PERMISSION_DENIED;
  1769. break;
  1770. }
  1771. }
  1772. //
  1773. // The calling process is also not allowed to already have a
  1774. // controlling terminal. Check the terminal list for a terminal with
  1775. // the current session.
  1776. //
  1777. KeAcquireQueuedLock(IoTerminalListLock);
  1778. ControllingTerminal = IopLookupTerminal(CurrentSessionId);
  1779. if (ControllingTerminal != NULL) {
  1780. Status = STATUS_PERMISSION_DENIED;
  1781. //
  1782. // If there is no controlling terminal for the session, then proceed to
  1783. // set the current terminal as the controlling terminal.
  1784. //
  1785. } else {
  1786. Status = STATUS_SUCCESS;
  1787. KeAcquireQueuedLock(Terminal->OutputLock);
  1788. KeAcquireQueuedLock(Terminal->InputLock);
  1789. if (Terminal->SessionId != TERMINAL_INVALID_SESSION) {
  1790. if (Terminal->SessionId != CurrentSessionId) {
  1791. Status = PsCheckPermission(PERMISSION_SYSTEM_ADMINISTRATOR);
  1792. }
  1793. }
  1794. if (KSUCCESS(Status)) {
  1795. if (Terminal->SessionId != TERMINAL_INVALID_SESSION) {
  1796. //
  1797. // Clear the controlling terminal in the process. If the
  1798. // race was won, this thread needs to release the reference
  1799. // on the terminal.
  1800. //
  1801. PreviousTerminal = PsSetControllingTerminal(
  1802. Terminal->SessionProcess,
  1803. NULL);
  1804. if (PreviousTerminal != NULL) {
  1805. ASSERT(PreviousTerminal == Terminal);
  1806. IoRelinquishTerminal(Terminal,
  1807. Terminal->SessionId,
  1808. TRUE);
  1809. } else {
  1810. IopRelinquishTerminal(Terminal);
  1811. }
  1812. }
  1813. //
  1814. // Try to set the controlling terminal in the new process. Add
  1815. // a reference that will be released when the controlling
  1816. // terminal is relinquished by the process.
  1817. //
  1818. if (PsSetControllingTerminal(Process, Terminal) == NULL) {
  1819. Terminal->SessionId = CurrentSessionId;
  1820. Terminal->ProcessGroupId = Terminal->SessionId;
  1821. Terminal->SessionProcess = Process;
  1822. ObAddReference(Terminal);
  1823. }
  1824. }
  1825. KeReleaseQueuedLock(Terminal->InputLock);
  1826. KeReleaseQueuedLock(Terminal->OutputLock);
  1827. }
  1828. KeReleaseQueuedLock(IoTerminalListLock);
  1829. break;
  1830. case TerminalControlGetCurrentSessionId:
  1831. //
  1832. // TODO: Fail TIOCGSID if the terminal is not a master pseudoterminal.
  1833. //
  1834. //
  1835. // The given terminal must be the controlling terminal of the calling
  1836. // process.
  1837. //
  1838. PsGetProcessGroup(NULL, &CurrentProcessGroupId, &CurrentSessionId);
  1839. Status = STATUS_SUCCESS;
  1840. KeAcquireQueuedLock(Terminal->OutputLock);
  1841. KeAcquireQueuedLock(Terminal->InputLock);
  1842. if (Terminal->SessionId != CurrentSessionId) {
  1843. Status = STATUS_NOT_A_TERMINAL;
  1844. } else {
  1845. SessionId = Terminal->SessionId;
  1846. }
  1847. KeReleaseQueuedLock(Terminal->InputLock);
  1848. KeReleaseQueuedLock(Terminal->OutputLock);
  1849. if (!KSUCCESS(Status)) {
  1850. break;
  1851. }
  1852. Status = IopTerminalUserBufferCopy(FromKernelMode,
  1853. FALSE,
  1854. ContextBuffer,
  1855. &SessionId,
  1856. sizeof(SESSION_ID));
  1857. break;
  1858. case TerminalControlGiveUpControllingTerminal:
  1859. Status = STATUS_SUCCESS;
  1860. Process = PsGetCurrentProcess();
  1861. PreviousTerminal = PsSetControllingTerminal(Process, NULL);
  1862. if (PreviousTerminal != NULL) {
  1863. PsGetProcessGroup(Process, NULL, &CurrentSessionId);
  1864. IoRelinquishTerminal(PreviousTerminal, CurrentSessionId, FALSE);
  1865. }
  1866. break;
  1867. case TerminalControlRedirectLocalConsole:
  1868. case TerminalControlSetPacketMode:
  1869. ASSERT(FALSE);
  1870. Status = STATUS_NOT_IMPLEMENTED;
  1871. break;
  1872. case TerminalControlSendBreakPosix:
  1873. case TerminalControlStartBreak:
  1874. case TerminalControlStopBreak:
  1875. Status = STATUS_SUCCESS;
  1876. break;
  1877. default:
  1878. Status = STATUS_NOT_SUPPORTED;
  1879. break;
  1880. }
  1881. //
  1882. // Also forward the request on to the physical device if there is one.
  1883. //
  1884. if ((KSUCCESS(Status)) && (Terminal->HardwareHandle != NULL)) {
  1885. HardwareStatus = IoUserControl(Terminal->HardwareHandle,
  1886. CodeNumber,
  1887. FromKernelMode,
  1888. ContextBuffer,
  1889. ContextBufferSize);
  1890. if (HardwareStatus != STATUS_NOT_HANDLED) {
  1891. Status = HardwareStatus;
  1892. }
  1893. }
  1894. return Status;
  1895. }
  1896. KSTATUS
  1897. IopTerminalFlush (
  1898. PFILE_OBJECT FileObject,
  1899. ULONG Flags
  1900. )
  1901. /*++
  1902. Routine Description:
  1903. This routine flushes a terminal object, discarding unwritten and unread
  1904. data.
  1905. Arguments:
  1906. FileObject - Supplies a pointer to the terminal to flush.
  1907. Flags - Supplies the flags governing the flush operation. See FLUSH_FLAG_*
  1908. definitions.
  1909. Return Value:
  1910. Status code.
  1911. --*/
  1912. {
  1913. BOOL AcceptingSignal;
  1914. PIO_OBJECT_STATE MasterIoState;
  1915. PROCESS_GROUP_ID ProcessGroup;
  1916. SESSION_ID Session;
  1917. PTERMINAL_SLAVE Slave;
  1918. PIO_OBJECT_STATE SlaveIoState;
  1919. KSTATUS Status;
  1920. PTERMINAL Terminal;
  1921. if (FileObject->Properties.Type == IoObjectTerminalSlave) {
  1922. Slave = FileObject->SpecialIo;
  1923. Terminal = NULL;
  1924. ASSERT(Slave->Header.Type == ObjectTerminalSlave);
  1925. Terminal = Slave->Master;
  1926. if (IO_IS_TERMINAL_MASTER_OPEN(Terminal) == FALSE) {
  1927. Status = STATUS_END_OF_FILE;
  1928. goto TerminalFlushEnd;
  1929. }
  1930. } else {
  1931. Terminal = FileObject->SpecialIo;
  1932. }
  1933. if (Terminal->SlaveFileObject == NULL) {
  1934. Status = STATUS_NOT_FOUND;
  1935. goto TerminalFlushEnd;
  1936. }
  1937. SlaveIoState = Terminal->SlaveFileObject->IoState;
  1938. MasterIoState = Terminal->MasterFileObject->IoState;
  1939. PsGetProcessGroup(NULL, &ProcessGroup, &Session);
  1940. if (Terminal->SlaveHandles == 0) {
  1941. Status = STATUS_NOT_READY;
  1942. goto TerminalFlushEnd;
  1943. }
  1944. //
  1945. // If the flushing process is not in the same process group, send the
  1946. // process group a signal unless the flushing process is ignoring or
  1947. // blocking that signal.
  1948. //
  1949. if ((ProcessGroup != Terminal->ProcessGroupId) &&
  1950. ((Terminal->Settings.LocalFlags &
  1951. TERMINAL_LOCAL_STOP_BACKGROUND_WRITES) != 0)) {
  1952. AcceptingSignal = PsIsThreadAcceptingSignal(
  1953. NULL,
  1954. SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
  1955. //
  1956. // If the process is accepting that signal, send it to it and tell it
  1957. // to try again later. The exception is an orphaned process group, in
  1958. // which case an error is returned. If the process is not accepting the
  1959. // signal, just let the flush go through.
  1960. //
  1961. if (AcceptingSignal != FALSE) {
  1962. if (PsIsProcessGroupOrphaned(ProcessGroup) != FALSE) {
  1963. Status = STATUS_DEVICE_IO_ERROR;
  1964. } else {
  1965. PsSignalProcessGroup(ProcessGroup,
  1966. SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
  1967. Status = STATUS_TRY_AGAIN;
  1968. }
  1969. goto TerminalFlushEnd;
  1970. }
  1971. }
  1972. //
  1973. // If discarding, reset the buffers.
  1974. //
  1975. if ((Flags & FLUSH_FLAG_DISCARD) != 0) {
  1976. if ((Flags & FLUSH_FLAG_READ) != 0) {
  1977. KeAcquireQueuedLock(Terminal->InputLock);
  1978. Terminal->InputBufferStart = 0;
  1979. Terminal->InputBufferEnd = 0;
  1980. IoSetIoObjectState(MasterIoState, POLL_EVENT_OUT, TRUE);
  1981. IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, FALSE);
  1982. KeReleaseQueuedLock(Terminal->InputLock);
  1983. KeAcquireQueuedLock(Terminal->WorkingInputLock);
  1984. Terminal->WorkingInputCursor = 0;
  1985. Terminal->WorkingInputLength = 0;
  1986. KeReleaseQueuedLock(Terminal->WorkingInputLock);
  1987. }
  1988. if ((Flags & FLUSH_FLAG_WRITE) != 0) {
  1989. KeAcquireQueuedLock(Terminal->OutputLock);
  1990. Terminal->OutputBufferStart = 0;
  1991. Terminal->OutputBufferEnd = 0;
  1992. IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, FALSE);
  1993. IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, TRUE);
  1994. KeReleaseQueuedLock(Terminal->OutputLock);
  1995. }
  1996. //
  1997. // If draining, wait for the output to go through.
  1998. //
  1999. } else {
  2000. //
  2001. // It doesn't make sense for the caller to try to flush a read, as
  2002. // they're the ones that need to flush.
  2003. //
  2004. if ((Flags & FLUSH_FLAG_READ) != 0) {
  2005. Status = STATUS_INVALID_PARAMETER;
  2006. goto TerminalFlushEnd;
  2007. }
  2008. //
  2009. // Wait for the write buffer to become empty.
  2010. //
  2011. if ((Flags & FLUSH_FLAG_WRITE) != 0) {
  2012. Status = STATUS_SUCCESS;
  2013. while (KSUCCESS(Status)) {
  2014. KeAcquireQueuedLock(Terminal->OutputLock);
  2015. //
  2016. // If the output is empty, then hooray, it's done.
  2017. //
  2018. if (Terminal->OutputBufferStart == Terminal->OutputBufferEnd) {
  2019. KeReleaseQueuedLock(Terminal->OutputLock);
  2020. break;
  2021. }
  2022. //
  2023. // Hijack the out event and unsignal it. When the master reads
  2024. // the data, it will signal it again.
  2025. //
  2026. IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, FALSE);
  2027. KeReleaseQueuedLock(Terminal->OutputLock);
  2028. Status = KeWaitForEvent(SlaveIoState->WriteEvent,
  2029. TRUE,
  2030. WAIT_TIME_INDEFINITE);
  2031. }
  2032. if (!KSUCCESS(Status)) {
  2033. goto TerminalFlushEnd;
  2034. }
  2035. }
  2036. }
  2037. Status = STATUS_SUCCESS;
  2038. TerminalFlushEnd:
  2039. return Status;
  2040. }
  2041. //
  2042. // --------------------------------------------------------- Internal Functions
  2043. //
  2044. KSTATUS
  2045. IopCreateTerminalObject (
  2046. FILE_PERMISSIONS CreatePermissions,
  2047. PTERMINAL *NewTerminal
  2048. )
  2049. /*++
  2050. Routine Description:
  2051. This routine creates a new terminal object.
  2052. Arguments:
  2053. CreatePermissions - Supplies the initial permissions to set on the slave
  2054. file object.
  2055. NewTerminal - Supplies a pointer where a pointer to a new terminal will be
  2056. returned on success.
  2057. Return Value:
  2058. Status code.
  2059. --*/
  2060. {
  2061. PCHAR ControlCharacters;
  2062. PTERMINAL_SLAVE Slave;
  2063. KSTATUS Status;
  2064. PTERMINAL Terminal;
  2065. //
  2066. // Create the terminal object. This references goes to the special I/O
  2067. // member of the file object on success.
  2068. //
  2069. Terminal = ObCreateObject(ObjectTerminalMaster,
  2070. IoTerminalDirectory,
  2071. NULL,
  2072. 0,
  2073. sizeof(TERMINAL),
  2074. IopDestroyTerminal,
  2075. 0,
  2076. TERMINAL_ALLOCATION_TAG);
  2077. if (Terminal == NULL) {
  2078. Status = STATUS_INSUFFICIENT_RESOURCES;
  2079. goto CreateTerminalObjectEnd;
  2080. }
  2081. //
  2082. // Initialize the terminal with an invalid number. The number is only used
  2083. // by terminals that are named in the terminal directory. Naming a terminal
  2084. // happens later with the appropriate synchronization.
  2085. //
  2086. Terminal->Number = MAX_ULONG;
  2087. //
  2088. // Set the master reference count to 1. This helps determine when the
  2089. // master is last closed by preventing new opens from succeeding if the
  2090. // master's reference goes to 0.
  2091. //
  2092. Terminal->MasterReferenceCount = 1;
  2093. //
  2094. // Allocate the input buffers.
  2095. //
  2096. Terminal->InputBuffer = MmAllocatePagedPool(TERMINAL_INPUT_BUFFER_SIZE,
  2097. TERMINAL_ALLOCATION_TAG);
  2098. if (Terminal->InputBuffer == NULL) {
  2099. Status = STATUS_INSUFFICIENT_RESOURCES;
  2100. goto CreateTerminalObjectEnd;
  2101. }
  2102. RtlZeroMemory(Terminal->InputBuffer, TERMINAL_INPUT_BUFFER_SIZE);
  2103. INITIALIZE_LIST_HEAD(&(Terminal->CommandHistory));
  2104. Terminal->WorkingInputBuffer = MmAllocatePagedPool(
  2105. TERMINAL_CANONICAL_BUFFER_SIZE,
  2106. TERMINAL_ALLOCATION_TAG);
  2107. if (Terminal->WorkingInputBuffer == NULL) {
  2108. Status = STATUS_INSUFFICIENT_RESOURCES;
  2109. goto CreateTerminalObjectEnd;
  2110. }
  2111. Terminal->WorkingInputLock = KeCreateQueuedLock();
  2112. if (Terminal->WorkingInputLock == NULL) {
  2113. Status = STATUS_INSUFFICIENT_RESOURCES;
  2114. goto CreateTerminalObjectEnd;
  2115. }
  2116. Terminal->InputLock = KeCreateQueuedLock();
  2117. if (Terminal->InputLock == NULL) {
  2118. Status = STATUS_INSUFFICIENT_RESOURCES;
  2119. goto CreateTerminalObjectEnd;
  2120. }
  2121. //
  2122. // Allocate the output buffers.
  2123. //
  2124. Terminal->OutputBuffer = MmAllocatePagedPool(TERMINAL_OUTPUT_BUFFER_SIZE,
  2125. TERMINAL_ALLOCATION_TAG);
  2126. if (Terminal->OutputBuffer == NULL) {
  2127. Status = STATUS_INSUFFICIENT_RESOURCES;
  2128. goto CreateTerminalObjectEnd;
  2129. }
  2130. Terminal->OutputLock = KeCreateQueuedLock();
  2131. if (Terminal->OutputLock == NULL) {
  2132. Status = STATUS_INSUFFICIENT_RESOURCES;
  2133. goto CreateTerminalObjectEnd;
  2134. }
  2135. //
  2136. // Set some default flags.
  2137. //
  2138. Terminal->Settings.LocalFlags = TERMINAL_LOCAL_ECHO |
  2139. TERMINAL_LOCAL_ECHO_ERASE |
  2140. TERMINAL_LOCAL_ECHO_KILL_NEWLINE |
  2141. TERMINAL_LOCAL_ECHO_KILL_EXTENDED |
  2142. TERMINAL_LOCAL_ECHO_NEWLINE |
  2143. TERMINAL_LOCAL_ECHO_CONTROL |
  2144. TERMINAL_LOCAL_CANONICAL |
  2145. TERMINAL_LOCAL_SIGNALS;
  2146. Terminal->Settings.InputFlags = TERMINAL_INPUT_CR_TO_NEWLINE;
  2147. Terminal->Settings.OutputFlags = TERMINAL_OUTPUT_POST_PROCESS |
  2148. TERMINAL_OUTPUT_NEWLINE_TO_CRLF;
  2149. Terminal->Settings.ControlFlags = TERMINAL_CONTROL_8_BITS_PER_CHARACTER;
  2150. ControlCharacters = Terminal->Settings.ControlCharacters;
  2151. ControlCharacters[TerminalCharacterEndOfFile] =
  2152. TERMINAL_DEFAULT_END_OF_FILE;
  2153. ControlCharacters[TerminalCharacterEndOfLine] =
  2154. TERMINAL_DEFAULT_END_OF_LINE;
  2155. ControlCharacters[TerminalCharacterErase] = TERMINAL_DEFAULT_ERASE;
  2156. ControlCharacters[TerminalCharacterInterrupt] = TERMINAL_DEFAULT_INTERRUPT;
  2157. ControlCharacters[TerminalCharacterKill] = TERMINAL_DEFAULT_KILL;
  2158. ControlCharacters[TerminalCharacterQuit] = TERMINAL_DEFAULT_QUIT;
  2159. ControlCharacters[TerminalCharacterSuspend] = TERMINAL_DEFAULT_SUSPEND;
  2160. ControlCharacters[TerminalCharacterStart] = TERMINAL_DEFAULT_START;
  2161. ControlCharacters[TerminalCharacterStop] = TERMINAL_DEFAULT_STOP;
  2162. ControlCharacters[TerminalCharacterFlushCount] = 1;
  2163. ControlCharacters[TerminalCharacterFlushTime] = 0;
  2164. Terminal->Settings.InputSpeed = TERMINAL_DEFAULT_BAUD_RATE;
  2165. Terminal->Settings.OutputSpeed = TERMINAL_DEFAULT_BAUD_RATE;
  2166. Terminal->WindowSize.Rows = TERMINAL_DEFAULT_ROWS;
  2167. Terminal->WindowSize.Columns = TERMINAL_DEFAULT_COLUMNS;
  2168. //
  2169. // Initialize the owning session and process group.
  2170. //
  2171. Terminal->SessionId = TERMINAL_INVALID_SESSION;
  2172. Terminal->ProcessGroupId = TERMINAL_INVALID_PROCESS_GROUP;
  2173. //
  2174. // Create the corresponding slave object.
  2175. //
  2176. Status = STATUS_SUCCESS;
  2177. Slave = ObCreateObject(ObjectTerminalSlave,
  2178. IoTerminalDirectory,
  2179. NULL,
  2180. 0,
  2181. sizeof(TERMINAL_SLAVE),
  2182. NULL,
  2183. 0,
  2184. TERMINAL_ALLOCATION_TAG);
  2185. if (Slave == NULL) {
  2186. Status = STATUS_INSUFFICIENT_RESOURCES;
  2187. goto CreateTerminalObjectEnd;
  2188. }
  2189. //
  2190. // Wire the master and slave together.
  2191. //
  2192. Terminal->Slave = Slave;
  2193. Slave->Master = Terminal;
  2194. //
  2195. // Add the terminal to the end of the list.
  2196. //
  2197. KeAcquireQueuedLock(IoTerminalListLock);
  2198. INSERT_BEFORE(&(Terminal->ListEntry), &IoTerminalList);
  2199. KeReleaseQueuedLock(IoTerminalListLock);
  2200. Status = STATUS_SUCCESS;
  2201. CreateTerminalObjectEnd:
  2202. if (!KSUCCESS(Status)) {
  2203. if (Terminal != NULL) {
  2204. ObReleaseReference(Terminal);
  2205. Terminal = NULL;
  2206. }
  2207. }
  2208. *NewTerminal = Terminal;
  2209. return Status;
  2210. }
  2211. VOID
  2212. IopDestroyTerminal (
  2213. PVOID TerminalObject
  2214. )
  2215. /*++
  2216. Routine Description:
  2217. This routine is called when an terminal master's reference count drops to
  2218. zero. It destroys all resources associated with the terminal. This occurs
  2219. well after all the slave has been freed.
  2220. Arguments:
  2221. TerminalObject - Supplies a pointer to the terminal object being destroyed.
  2222. Return Value:
  2223. None.
  2224. --*/
  2225. {
  2226. PTERMINAL_HISTORY_ENTRY HistoryEntry;
  2227. PTERMINAL Terminal;
  2228. Terminal = (PTERMINAL)TerminalObject;
  2229. ASSERT(Terminal->SlavePathPoint.PathEntry == NULL);
  2230. //
  2231. // If the slave never got a file object, then the master still has a
  2232. // reference on the slave it needs to release.
  2233. //
  2234. if (Terminal->SlaveFileObject == NULL) {
  2235. ObReleaseReference(Terminal->Slave);
  2236. }
  2237. if (Terminal->ListEntry.Next != NULL) {
  2238. KeAcquireQueuedLock(IoTerminalListLock);
  2239. LIST_REMOVE(&(Terminal->ListEntry));
  2240. KeReleaseQueuedLock(IoTerminalListLock);
  2241. }
  2242. if (Terminal->HardwareHandle != NULL) {
  2243. IoClose(Terminal->HardwareHandle);
  2244. Terminal->HardwareHandle = NULL;
  2245. }
  2246. while (LIST_EMPTY(&(Terminal->CommandHistory)) == FALSE) {
  2247. HistoryEntry = LIST_VALUE(Terminal->CommandHistory.Next,
  2248. TERMINAL_HISTORY_ENTRY,
  2249. ListEntry);
  2250. LIST_REMOVE(&(HistoryEntry->ListEntry));
  2251. MmFreePagedPool(HistoryEntry);
  2252. }
  2253. if (Terminal->InputBuffer != NULL) {
  2254. MmFreePagedPool(Terminal->InputBuffer);
  2255. }
  2256. if (Terminal->WorkingInputBuffer != NULL) {
  2257. MmFreePagedPool(Terminal->WorkingInputBuffer);
  2258. }
  2259. if (Terminal->WorkingInputLock != NULL) {
  2260. KeDestroyQueuedLock(Terminal->WorkingInputLock);
  2261. }
  2262. if (Terminal->InputLock != NULL) {
  2263. KeDestroyQueuedLock(Terminal->InputLock);
  2264. }
  2265. if (Terminal->OutputBuffer != NULL) {
  2266. MmFreePagedPool(Terminal->OutputBuffer);
  2267. }
  2268. if (Terminal->OutputLock != NULL) {
  2269. KeDestroyQueuedLock(Terminal->OutputLock);
  2270. }
  2271. return;
  2272. }
  2273. KSTATUS
  2274. IopTerminalMasterWrite (
  2275. PFILE_OBJECT FileObject,
  2276. PIO_CONTEXT IoContext
  2277. )
  2278. /*++
  2279. Routine Description:
  2280. This routine writes data to the terminal slave (data that will come out
  2281. the slave's standard input).
  2282. Arguments:
  2283. FileObject - Supplies a pointer to the terminal master file object.
  2284. IoContext - Supplies a pointer to the I/O context.
  2285. Return Value:
  2286. Status code. A failing status code does not necessarily mean no I/O made it
  2287. in or out. Check the bytes completed value in the I/O context to find out
  2288. how much occurred.
  2289. --*/
  2290. {
  2291. BOOL AddCharacter;
  2292. UCHAR Byte;
  2293. UINTN ByteIndex;
  2294. UCHAR Bytes[2];
  2295. UINTN BytesSize;
  2296. BOOL CharacterHandled;
  2297. PCHAR ControlCharacters;
  2298. ULONG DirtyRegionBegin;
  2299. ULONG DirtyRegionEnd;
  2300. ULONG EchoFlags;
  2301. BOOL EchoThisCharacter;
  2302. BOOL InputAdded;
  2303. ULONG InputFlags;
  2304. BOOL InputLockHeld;
  2305. BOOL IsEndOfLine;
  2306. UINTN LocalByteIndex;
  2307. CHAR LocalBytes[64];
  2308. UINTN LocalByteSize;
  2309. ULONG LocalFlags;
  2310. PIO_OBJECT_STATE MasterIoState;
  2311. ULONG MoveIndex;
  2312. BOOL OutputLockHeld;
  2313. BOOL OutputWritten;
  2314. ULONG ReturnedEvents;
  2315. ULONG ScreenCursorPosition;
  2316. PIO_OBJECT_STATE SlaveIoState;
  2317. ULONG Space;
  2318. KSTATUS Status;
  2319. PTERMINAL Terminal;
  2320. ULONG TimeoutInMilliseconds;
  2321. BOOL TransferWorkingBuffer;
  2322. Terminal = FileObject->SpecialIo;
  2323. ASSERT(Terminal->Header.Type == ObjectTerminalMaster);
  2324. ASSERT(FileObject == Terminal->MasterFileObject);
  2325. MasterIoState = FileObject->IoState;
  2326. if (Terminal->SlaveFileObject == NULL) {
  2327. IoContext->BytesCompleted = 0;
  2328. return STATUS_NOT_READY;
  2329. }
  2330. SlaveIoState = Terminal->SlaveFileObject->IoState;
  2331. InputFlags = Terminal->Settings.InputFlags;
  2332. LocalFlags = Terminal->Settings.LocalFlags;
  2333. EchoFlags = TERMINAL_LOCAL_ECHO | TERMINAL_LOCAL_ECHO_ERASE |
  2334. TERMINAL_LOCAL_ECHO_KILL_NEWLINE |
  2335. TERMINAL_LOCAL_ECHO_KILL_EXTENDED |
  2336. TERMINAL_LOCAL_ECHO_NEWLINE |
  2337. TERMINAL_LOCAL_ECHO_CONTROL;
  2338. EchoFlags = EchoFlags & LocalFlags;
  2339. ControlCharacters = Terminal->Settings.ControlCharacters;
  2340. InputAdded = FALSE;
  2341. InputLockHeld = FALSE;
  2342. DirtyRegionBegin = Terminal->WorkingInputCursor;
  2343. DirtyRegionEnd = Terminal->WorkingInputCursor;
  2344. OutputLockHeld = FALSE;
  2345. OutputWritten = FALSE;
  2346. ScreenCursorPosition = Terminal->WorkingInputCursor;
  2347. TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  2348. if (EchoFlags != 0) {
  2349. KeAcquireQueuedLock(Terminal->OutputLock);
  2350. OutputLockHeld = TRUE;
  2351. }
  2352. //
  2353. // In canonical mode, acquire the lock to prevent others from using the
  2354. // working buffer.
  2355. //
  2356. if ((LocalFlags & TERMINAL_LOCAL_CANONICAL) != 0) {
  2357. KeAcquireQueuedLock(Terminal->WorkingInputLock);
  2358. //
  2359. // In raw mode, acquire the input lock now to avoid locking and unlocking
  2360. // for every character.
  2361. //
  2362. } else {
  2363. KeAcquireQueuedLock(Terminal->InputLock);
  2364. InputLockHeld = TRUE;
  2365. }
  2366. //
  2367. // Loop through every byte.
  2368. //
  2369. LocalByteIndex = 0;
  2370. LocalByteSize = 0;
  2371. for (ByteIndex = 0; ByteIndex < IoContext->SizeInBytes; ByteIndex += 1) {
  2372. TransferWorkingBuffer = FALSE;
  2373. AddCharacter = TRUE;
  2374. //
  2375. // Get the particular byte in question. Keep a local bounce buffer to
  2376. // avoid calling the copy I/O buffer data function for every single
  2377. // byte.
  2378. //
  2379. if (LocalByteIndex < LocalByteSize) {
  2380. Byte = LocalBytes[LocalByteIndex];
  2381. LocalByteIndex += 1;
  2382. } else {
  2383. LocalByteSize = IoContext->SizeInBytes - ByteIndex;
  2384. if (LocalByteSize > sizeof(LocalBytes)) {
  2385. LocalByteSize = sizeof(LocalBytes);
  2386. }
  2387. Status = MmCopyIoBufferData(IoContext->IoBuffer,
  2388. LocalBytes,
  2389. ByteIndex,
  2390. LocalByteSize,
  2391. FALSE);
  2392. if (!KSUCCESS(Status)) {
  2393. goto TerminalMasterWriteEnd;
  2394. }
  2395. Byte = LocalBytes[0];
  2396. LocalByteIndex = 1;
  2397. }
  2398. //
  2399. // Grab the output lock if it's not already held.
  2400. //
  2401. if ((OutputLockHeld == FALSE) && (EchoFlags != 0)) {
  2402. KeAcquireQueuedLock(Terminal->OutputLock);
  2403. OutputLockHeld = TRUE;
  2404. }
  2405. //
  2406. // Process signal generating characters.
  2407. //
  2408. if (Byte == ControlCharacters[TerminalCharacterInterrupt]) {
  2409. if ((LocalFlags & TERMINAL_LOCAL_SIGNALS) != 0) {
  2410. AddCharacter = FALSE;
  2411. if ((Terminal->SlaveHandles != 0) &&
  2412. (Terminal->ProcessGroupId !=
  2413. TERMINAL_INVALID_PROCESS_GROUP)) {
  2414. PsSignalProcessGroup(Terminal->ProcessGroupId,
  2415. SIGNAL_KEYBOARD_INTERRUPT);
  2416. }
  2417. }
  2418. }
  2419. if (Byte == ControlCharacters[TerminalCharacterQuit]) {
  2420. if ((LocalFlags & TERMINAL_LOCAL_SIGNALS) != 0) {
  2421. AddCharacter = FALSE;
  2422. if ((Terminal->SlaveHandles != 0) &&
  2423. (Terminal->ProcessGroupId !=
  2424. TERMINAL_INVALID_PROCESS_GROUP)) {
  2425. PsSignalProcessGroup(Terminal->ProcessGroupId,
  2426. SIGNAL_REQUEST_CORE_DUMP);
  2427. }
  2428. }
  2429. }
  2430. //
  2431. // Run through the input flags.
  2432. //
  2433. if ((InputFlags & TERMINAL_INPUT_STRIP) != 0) {
  2434. Byte &= 0x7F;
  2435. }
  2436. if (Byte == '\r') {
  2437. if ((InputFlags & TERMINAL_INPUT_CR_TO_NEWLINE) != 0) {
  2438. Byte = '\n';
  2439. } else if ((InputFlags & TERMINAL_INPUT_IGNORE_CR) != 0) {
  2440. AddCharacter = FALSE;
  2441. }
  2442. } else if (Byte == '\n') {
  2443. if ((InputFlags & TERMINAL_INPUT_NEWLINE_TO_CR) != 0) {
  2444. Byte = '\r';
  2445. }
  2446. }
  2447. //
  2448. // Process the byte in cooked mode.
  2449. //
  2450. if ((LocalFlags & TERMINAL_LOCAL_CANONICAL) != 0) {
  2451. IsEndOfLine = FALSE;
  2452. //
  2453. // First let an editing function take a look at it.
  2454. //
  2455. CharacterHandled = IopTerminalProcessEditingCharacter(
  2456. Terminal,
  2457. Byte,
  2458. TimeoutInMilliseconds,
  2459. &DirtyRegionBegin,
  2460. &DirtyRegionEnd,
  2461. &ScreenCursorPosition,
  2462. &OutputWritten);
  2463. if (CharacterHandled != FALSE) {
  2464. AddCharacter = FALSE;
  2465. //
  2466. // Pushing return transfers the working buffer to the slave's input.
  2467. //
  2468. } else if ((Byte ==
  2469. ControlCharacters[TerminalCharacterEndOfLine]) ||
  2470. (Byte == '\n')) {
  2471. TransferWorkingBuffer = TRUE;
  2472. Terminal->WorkingInputCursor = Terminal->WorkingInputLength;
  2473. IsEndOfLine = TRUE;
  2474. //
  2475. // End of file is like pushing enter except there's no enter
  2476. // character.
  2477. //
  2478. } else if (Byte == ControlCharacters[TerminalCharacterEndOfFile]) {
  2479. TransferWorkingBuffer = TRUE;
  2480. AddCharacter = FALSE;
  2481. }
  2482. //
  2483. // Add the character to the working buffer if needed.
  2484. //
  2485. if (AddCharacter != FALSE) {
  2486. if (Terminal->WorkingInputLength !=
  2487. TERMINAL_CANONICAL_BUFFER_SIZE) {
  2488. if (Terminal->WorkingInputCursor < DirtyRegionBegin) {
  2489. DirtyRegionBegin = Terminal->WorkingInputCursor;
  2490. }
  2491. //
  2492. // Make a hole.
  2493. //
  2494. for (MoveIndex = Terminal->WorkingInputLength;
  2495. MoveIndex > Terminal->WorkingInputCursor;
  2496. MoveIndex -= 1) {
  2497. Terminal->WorkingInputBuffer[MoveIndex] =
  2498. Terminal->WorkingInputBuffer[MoveIndex - 1];
  2499. }
  2500. Terminal->WorkingInputBuffer[Terminal->WorkingInputCursor] =
  2501. Byte;
  2502. Terminal->WorkingInputCursor += 1;
  2503. Terminal->WorkingInputLength += 1;
  2504. if ((IsEndOfLine == FALSE) &&
  2505. (Terminal->WorkingInputLength > DirtyRegionEnd)) {
  2506. DirtyRegionEnd = Terminal->WorkingInputLength;
  2507. Terminal->Flags &= ~(TERMINAL_FLAG_VIRGIN_LINE |
  2508. TERMINAL_FLAG_UNEDITED_LINE);
  2509. }
  2510. }
  2511. }
  2512. //
  2513. // Flush the buffer if desired.
  2514. //
  2515. if (TransferWorkingBuffer != FALSE) {
  2516. //
  2517. // Fix up the line before abandoning it.
  2518. //
  2519. if ((DirtyRegionBegin != DirtyRegionEnd) &&
  2520. ((EchoFlags & TERMINAL_LOCAL_ECHO) != 0)) {
  2521. if (OutputLockHeld == FALSE) {
  2522. KeAcquireQueuedLock(Terminal->OutputLock);
  2523. OutputLockHeld = TRUE;
  2524. }
  2525. IopTerminalFixUpCanonicalLine(Terminal,
  2526. TimeoutInMilliseconds,
  2527. DirtyRegionBegin,
  2528. DirtyRegionEnd,
  2529. ScreenCursorPosition);
  2530. ScreenCursorPosition = Terminal->WorkingInputCursor;
  2531. OutputWritten = TRUE;
  2532. }
  2533. //
  2534. // Wait for there to be enough space.
  2535. //
  2536. while (TRUE) {
  2537. ASSERT(InputLockHeld == FALSE);
  2538. KeAcquireQueuedLock(Terminal->InputLock);
  2539. InputLockHeld = TRUE;
  2540. Space = IopTerminalGetInputBufferSpace(Terminal);
  2541. if (Space >= Terminal->WorkingInputLength) {
  2542. break;
  2543. }
  2544. IoSetIoObjectState(MasterIoState, POLL_EVENT_OUT, FALSE);
  2545. IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, TRUE);
  2546. InputAdded = FALSE;
  2547. KeReleaseQueuedLock(Terminal->InputLock);
  2548. InputLockHeld = FALSE;
  2549. Status = IoWaitForIoObjectState(MasterIoState,
  2550. POLL_EVENT_OUT,
  2551. TRUE,
  2552. TimeoutInMilliseconds,
  2553. &ReturnedEvents);
  2554. if (!KSUCCESS(Status)) {
  2555. goto TerminalMasterWriteEnd;
  2556. }
  2557. if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
  2558. Status = STATUS_DEVICE_IO_ERROR;
  2559. goto TerminalMasterWriteEnd;
  2560. }
  2561. }
  2562. //
  2563. // Move the bytes to the input buffer.
  2564. //
  2565. for (MoveIndex = 0;
  2566. MoveIndex < Terminal->WorkingInputLength;
  2567. MoveIndex += 1) {
  2568. Terminal->InputBuffer[Terminal->InputBufferEnd] =
  2569. Terminal->WorkingInputBuffer[MoveIndex];
  2570. Terminal->InputBufferEnd += 1;
  2571. if (Terminal->InputBufferEnd ==
  2572. TERMINAL_INPUT_BUFFER_SIZE) {
  2573. Terminal->InputBufferEnd = 0;
  2574. }
  2575. ASSERT(Terminal->InputBufferEnd !=
  2576. Terminal->InputBufferStart);
  2577. }
  2578. KeReleaseQueuedLock(Terminal->InputLock);
  2579. InputLockHeld = FALSE;
  2580. IopTerminalAddHistoryEntry(Terminal);
  2581. InputAdded = TRUE;
  2582. Terminal->WorkingInputCursor = 0;
  2583. Terminal->WorkingInputLength = 0;
  2584. DirtyRegionBegin = 0;
  2585. DirtyRegionEnd = 0;
  2586. ScreenCursorPosition = 0;
  2587. Terminal->Flags |= TERMINAL_FLAG_VIRGIN_LINE |
  2588. TERMINAL_FLAG_UNEDITED_LINE;
  2589. }
  2590. //
  2591. // Input is not canonical, it just goes directly in the input buffer.
  2592. //
  2593. } else {
  2594. if (AddCharacter == FALSE) {
  2595. continue;
  2596. }
  2597. //
  2598. // Wait if there's not enough space available.
  2599. //
  2600. while (IopTerminalGetInputBufferSpace(Terminal) == 0) {
  2601. IoSetIoObjectState(MasterIoState, POLL_EVENT_OUT, FALSE);
  2602. IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, TRUE);
  2603. KeReleaseQueuedLock(Terminal->InputLock);
  2604. InputLockHeld = FALSE;
  2605. InputAdded = FALSE;
  2606. Status = IoWaitForIoObjectState(MasterIoState,
  2607. POLL_EVENT_OUT,
  2608. TRUE,
  2609. TimeoutInMilliseconds,
  2610. &ReturnedEvents);
  2611. if (!KSUCCESS(Status)) {
  2612. goto TerminalMasterWriteEnd;
  2613. }
  2614. if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
  2615. Status = STATUS_DEVICE_IO_ERROR;
  2616. goto TerminalMasterWriteEnd;
  2617. }
  2618. KeAcquireQueuedLock(Terminal->InputLock);
  2619. InputLockHeld = TRUE;
  2620. }
  2621. //
  2622. // Add the character to the input buffer.
  2623. //
  2624. Terminal->InputBuffer[Terminal->InputBufferEnd] = Byte;
  2625. Terminal->InputBufferEnd += 1;
  2626. if (Terminal->InputBufferEnd == TERMINAL_INPUT_BUFFER_SIZE) {
  2627. Terminal->InputBufferEnd = 0;
  2628. }
  2629. ASSERT(Terminal->InputBufferEnd != Terminal->InputBufferStart);
  2630. InputAdded = TRUE;
  2631. }
  2632. //
  2633. // Potentially echo the byte. Failure to echo is not necessarily
  2634. // considered a failure.
  2635. //
  2636. if (EchoFlags != 0) {
  2637. //
  2638. // In raw mode, echo everything unless disallowed.
  2639. //
  2640. if ((LocalFlags & TERMINAL_LOCAL_CANONICAL) == 0) {
  2641. EchoThisCharacter = FALSE;
  2642. if ((EchoFlags & TERMINAL_LOCAL_ECHO) != 0) {
  2643. EchoThisCharacter = TRUE;
  2644. } else if ((Byte == '\n') &&
  2645. ((EchoFlags & TERMINAL_LOCAL_ECHO_NEWLINE) != 0)) {
  2646. EchoThisCharacter = TRUE;
  2647. }
  2648. //
  2649. // In canonical mode, only consider echoing newlines. Everything
  2650. // else is handled automatically.
  2651. //
  2652. } else {
  2653. EchoThisCharacter = FALSE;
  2654. if (Byte == '\n') {
  2655. if ((EchoFlags &
  2656. (TERMINAL_LOCAL_ECHO_NEWLINE |
  2657. TERMINAL_LOCAL_ECHO)) != 0) {
  2658. EchoThisCharacter = TRUE;
  2659. }
  2660. }
  2661. }
  2662. if (EchoThisCharacter != FALSE) {
  2663. Bytes[0] = Byte;
  2664. BytesSize = 1;
  2665. if ((Byte < ' ') &&
  2666. ((EchoFlags & TERMINAL_LOCAL_ECHO_CONTROL) != 0) &&
  2667. (!RtlIsCharacterSpace(Byte)) && (Byte != '\0')) {
  2668. Bytes[1] = Byte + '@';
  2669. Bytes[0] = '^';
  2670. BytesSize = 2;
  2671. }
  2672. IopTerminalWriteOutputBuffer(Terminal,
  2673. Bytes,
  2674. BytesSize,
  2675. 1,
  2676. TimeoutInMilliseconds);
  2677. OutputWritten = TRUE;
  2678. }
  2679. }
  2680. }
  2681. //
  2682. // In canonical mode, the line may need to be fixed up.
  2683. //
  2684. if ((DirtyRegionBegin != DirtyRegionEnd) &&
  2685. ((EchoFlags & TERMINAL_LOCAL_ECHO) != 0)) {
  2686. if (OutputLockHeld == FALSE) {
  2687. KeAcquireQueuedLock(Terminal->OutputLock);
  2688. OutputLockHeld = TRUE;
  2689. }
  2690. IopTerminalFixUpCanonicalLine(Terminal,
  2691. TimeoutInMilliseconds,
  2692. DirtyRegionBegin,
  2693. DirtyRegionEnd,
  2694. ScreenCursorPosition);
  2695. OutputWritten = TRUE;
  2696. }
  2697. Status = STATUS_SUCCESS;
  2698. TerminalMasterWriteEnd:
  2699. //
  2700. // Signal the input and/or output that there's stuff to do.
  2701. //
  2702. if (OutputWritten != FALSE) {
  2703. if (OutputLockHeld == FALSE) {
  2704. KeAcquireQueuedLock(Terminal->OutputLock);
  2705. OutputLockHeld = TRUE;
  2706. }
  2707. IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, TRUE);
  2708. }
  2709. if (InputAdded != FALSE) {
  2710. if (InputLockHeld == FALSE) {
  2711. KeAcquireQueuedLock(Terminal->InputLock);
  2712. InputLockHeld = TRUE;
  2713. }
  2714. IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, TRUE);
  2715. }
  2716. //
  2717. // Release the various locks that are held.
  2718. //
  2719. if ((LocalFlags & TERMINAL_LOCAL_CANONICAL) != 0) {
  2720. KeReleaseQueuedLock(Terminal->WorkingInputLock);
  2721. }
  2722. if (InputLockHeld != FALSE) {
  2723. KeReleaseQueuedLock(Terminal->InputLock);
  2724. }
  2725. if (OutputLockHeld != FALSE) {
  2726. KeReleaseQueuedLock(Terminal->OutputLock);
  2727. }
  2728. IoContext->BytesCompleted = ByteIndex;
  2729. return Status;
  2730. }
  2731. KSTATUS
  2732. IopTerminalSlaveWrite (
  2733. PFILE_OBJECT FileObject,
  2734. PIO_CONTEXT IoContext
  2735. )
  2736. /*++
  2737. Routine Description:
  2738. This routine writes data to the terminal master (ie writes to the slaves
  2739. standard out).
  2740. Arguments:
  2741. FileObject - Supplies a pointer to the slave terminal file object.
  2742. IoContext - Supplies a pointer to the I/O context.
  2743. Return Value:
  2744. Status code. A failing status code does not necessarily mean no I/O made it
  2745. in or out. Check the bytes completed value in the I/O context to find out
  2746. how much occurred.
  2747. --*/
  2748. {
  2749. BOOL AcceptingSignal;
  2750. BOOL AnythingWritten;
  2751. UINTN BytesThisRound;
  2752. UINTN BytesWritten;
  2753. UCHAR LocalBytes[64];
  2754. BOOL LockHeld;
  2755. PIO_OBJECT_STATE MasterIoState;
  2756. PROCESS_GROUP_ID ProcessGroup;
  2757. ULONG ReturnedEvents;
  2758. SESSION_ID Session;
  2759. BOOL SignalProcessGroup;
  2760. PTERMINAL_SLAVE Slave;
  2761. PIO_OBJECT_STATE SlaveIoState;
  2762. ULONG Space;
  2763. KSTATUS Status;
  2764. PTERMINAL Terminal;
  2765. ULONG TimeoutInMilliseconds;
  2766. AnythingWritten = FALSE;
  2767. BytesWritten = 0;
  2768. LockHeld = FALSE;
  2769. MasterIoState = NULL;
  2770. SignalProcessGroup = FALSE;
  2771. Slave = FileObject->SpecialIo;
  2772. Terminal = Slave->Master;
  2773. TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  2774. ASSERT(Slave->Header.Type == ObjectTerminalSlave);
  2775. if (IO_IS_TERMINAL_MASTER_OPEN(Terminal) == FALSE) {
  2776. Status = STATUS_BROKEN_PIPE;
  2777. goto TerminalSlaveWriteEnd;
  2778. }
  2779. SlaveIoState = Terminal->SlaveFileObject->IoState;
  2780. MasterIoState = Terminal->MasterFileObject->IoState;
  2781. PsGetProcessGroup(NULL, &ProcessGroup, &Session);
  2782. //
  2783. // Synchronize the checks on the terminal attachment and the owning session
  2784. // and process group with the IOCTLs that may modify them.
  2785. //
  2786. KeAcquireQueuedLock(Terminal->OutputLock);
  2787. LockHeld = TRUE;
  2788. //
  2789. // If the writing process is not in the same process group, send the
  2790. // process group a signal unless the writing process is ignoring or
  2791. // blocking that signal.
  2792. //
  2793. if ((ProcessGroup != Terminal->ProcessGroupId) &&
  2794. ((Terminal->Settings.LocalFlags &
  2795. TERMINAL_LOCAL_STOP_BACKGROUND_WRITES) != 0)) {
  2796. AcceptingSignal = PsIsThreadAcceptingSignal(
  2797. NULL,
  2798. SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
  2799. //
  2800. // If the process is accepting that signal, send it to it and tell it
  2801. // to try again later. The exception is an orphaned process group, in
  2802. // which case an error is returned. If the process is not accepting the
  2803. // signal, just let the write go through.
  2804. //
  2805. if (AcceptingSignal != FALSE) {
  2806. if (PsIsProcessGroupOrphaned(ProcessGroup) != FALSE) {
  2807. Status = STATUS_DEVICE_IO_ERROR;
  2808. } else {
  2809. SignalProcessGroup = TRUE;
  2810. Status = STATUS_TRY_AGAIN;
  2811. }
  2812. goto TerminalSlaveWriteEnd;
  2813. }
  2814. }
  2815. //
  2816. // Loop writing bytes until it's done.
  2817. //
  2818. Status = STATUS_SUCCESS;
  2819. Space = IopTerminalGetOutputBufferSpace(Terminal);
  2820. while (BytesWritten != IoContext->SizeInBytes) {
  2821. //
  2822. // If there's no space, release the lock and wait for space to open up.
  2823. //
  2824. if (Space == 0) {
  2825. IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, TRUE);
  2826. IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, FALSE);
  2827. KeReleaseQueuedLock(Terminal->OutputLock);
  2828. LockHeld = FALSE;
  2829. Status = IoWaitForIoObjectState(SlaveIoState,
  2830. POLL_EVENT_OUT,
  2831. TRUE,
  2832. TimeoutInMilliseconds,
  2833. &ReturnedEvents);
  2834. if (!KSUCCESS(Status)) {
  2835. goto TerminalSlaveWriteEnd;
  2836. }
  2837. if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
  2838. Status = STATUS_DEVICE_IO_ERROR;
  2839. goto TerminalSlaveWriteEnd;
  2840. }
  2841. KeAcquireQueuedLock(Terminal->OutputLock);
  2842. LockHeld = TRUE;
  2843. Space = IopTerminalGetOutputBufferSpace(Terminal);
  2844. continue;
  2845. }
  2846. BytesThisRound = Space;
  2847. if (IoContext->SizeInBytes - BytesWritten < Space) {
  2848. BytesThisRound = IoContext->SizeInBytes - BytesWritten;
  2849. }
  2850. //
  2851. // Copy the data from the I/O buffer to a local bounce buffer, then
  2852. // into the output buffer.
  2853. //
  2854. if (BytesThisRound > sizeof(LocalBytes)) {
  2855. BytesThisRound = sizeof(LocalBytes);
  2856. }
  2857. Status = MmCopyIoBufferData(IoContext->IoBuffer,
  2858. LocalBytes,
  2859. BytesWritten,
  2860. BytesThisRound,
  2861. FALSE);
  2862. if (!KSUCCESS(Status)) {
  2863. break;
  2864. }
  2865. Status = IopTerminalWriteOutputBuffer(Terminal,
  2866. LocalBytes,
  2867. BytesThisRound,
  2868. 1,
  2869. TimeoutInMilliseconds);
  2870. if (!KSUCCESS(Status)) {
  2871. goto TerminalSlaveWriteEnd;
  2872. }
  2873. Space = IopTerminalGetOutputBufferSpace(Terminal);
  2874. AnythingWritten = TRUE;
  2875. BytesWritten += BytesThisRound;
  2876. }
  2877. //
  2878. // Unsignal the write event if this routine just wrote the last of the
  2879. // space.
  2880. //
  2881. ASSERT(LockHeld != FALSE);
  2882. if ((AnythingWritten != FALSE) && (Space == 0)) {
  2883. IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, TRUE);
  2884. IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, FALSE);
  2885. }
  2886. TerminalSlaveWriteEnd:
  2887. if (AnythingWritten != FALSE) {
  2888. if (LockHeld == FALSE) {
  2889. KeAcquireQueuedLock(Terminal->OutputLock);
  2890. LockHeld = TRUE;
  2891. }
  2892. IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, TRUE);
  2893. }
  2894. if (LockHeld != FALSE) {
  2895. KeReleaseQueuedLock(Terminal->OutputLock);
  2896. }
  2897. if (SignalProcessGroup != FALSE) {
  2898. PsSignalProcessGroup(ProcessGroup, SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
  2899. }
  2900. IoContext->BytesCompleted = BytesWritten;
  2901. return Status;
  2902. }
  2903. KSTATUS
  2904. IopTerminalMasterRead (
  2905. PFILE_OBJECT FileObject,
  2906. PIO_CONTEXT IoContext
  2907. )
  2908. /*++
  2909. Routine Description:
  2910. This routine reads data from the master side (the slave's standard out).
  2911. Arguments:
  2912. FileObject - Supplies a pointer to the master terminal file object.
  2913. IoContext - Supplies a pointer to the I/O context.
  2914. Return Value:
  2915. Status code. A failing status code does not necessarily mean no I/O made it
  2916. in or out. Check the bytes completed value in the I/O context to find out
  2917. how much occurred.
  2918. --*/
  2919. {
  2920. BOOL AnythingRead;
  2921. UINTN BytesRead;
  2922. UINTN CopySize;
  2923. BOOL LockHeld;
  2924. PIO_OBJECT_STATE MasterIoState;
  2925. ULONG ReturnedEvents;
  2926. PIO_OBJECT_STATE SlaveIoState;
  2927. ULONG Space;
  2928. KSTATUS Status;
  2929. PTERMINAL Terminal;
  2930. ULONG TimeoutInMilliseconds;
  2931. Terminal = FileObject->SpecialIo;
  2932. ASSERT(Terminal->Header.Type == ObjectTerminalMaster);
  2933. ASSERT(Terminal->MasterFileObject == FileObject);
  2934. if (Terminal->SlaveFileObject == NULL) {
  2935. IoContext->BytesCompleted = 0;
  2936. return STATUS_NOT_READY;
  2937. }
  2938. SlaveIoState = Terminal->SlaveFileObject->IoState;
  2939. MasterIoState = FileObject->IoState;
  2940. TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  2941. AnythingRead = FALSE;
  2942. BytesRead = 0;
  2943. KeAcquireQueuedLock(Terminal->OutputLock);
  2944. LockHeld = TRUE;
  2945. Space = IopTerminalGetOutputBufferSpace(Terminal);
  2946. while (BytesRead < IoContext->SizeInBytes) {
  2947. //
  2948. // Wait for data to be ready.
  2949. //
  2950. while (Space == TERMINAL_OUTPUT_BUFFER_SIZE - 1) {
  2951. //
  2952. // If the caller got something already, just return immediately
  2953. // instead of waiting for the full buffer amount.
  2954. //
  2955. if (AnythingRead != FALSE) {
  2956. Status = STATUS_SUCCESS;
  2957. goto TerminalMasterReadEnd;
  2958. }
  2959. IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, FALSE);
  2960. IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, TRUE);
  2961. KeReleaseQueuedLock(Terminal->OutputLock);
  2962. LockHeld = FALSE;
  2963. Status = IoWaitForIoObjectState(MasterIoState,
  2964. POLL_EVENT_IN,
  2965. TRUE,
  2966. TimeoutInMilliseconds,
  2967. &ReturnedEvents);
  2968. if (!KSUCCESS(Status)) {
  2969. goto TerminalMasterReadEnd;
  2970. }
  2971. if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
  2972. Status = STATUS_DEVICE_IO_ERROR;
  2973. goto TerminalMasterReadEnd;
  2974. }
  2975. KeAcquireQueuedLock(Terminal->OutputLock);
  2976. LockHeld = TRUE;
  2977. Space = IopTerminalGetOutputBufferSpace(Terminal);
  2978. }
  2979. //
  2980. // Copy the bytes out. Don't wrap across the terminal's circular buffer.
  2981. //
  2982. CopySize = (TERMINAL_OUTPUT_BUFFER_SIZE - 1) - Space;
  2983. if (CopySize > IoContext->SizeInBytes - BytesRead) {
  2984. CopySize = IoContext->SizeInBytes - BytesRead;
  2985. }
  2986. if (CopySize >
  2987. TERMINAL_OUTPUT_BUFFER_SIZE - Terminal->OutputBufferStart) {
  2988. CopySize = TERMINAL_OUTPUT_BUFFER_SIZE -
  2989. Terminal->OutputBufferStart;
  2990. }
  2991. Status = MmCopyIoBufferData(
  2992. IoContext->IoBuffer,
  2993. Terminal->OutputBuffer + Terminal->OutputBufferStart,
  2994. BytesRead,
  2995. CopySize,
  2996. TRUE);
  2997. if (!KSUCCESS(Status)) {
  2998. goto TerminalMasterReadEnd;
  2999. }
  3000. Terminal->OutputBufferStart += CopySize;
  3001. ASSERT(Terminal->OutputBufferStart <= TERMINAL_OUTPUT_BUFFER_SIZE);
  3002. if (Terminal->OutputBufferStart == TERMINAL_OUTPUT_BUFFER_SIZE) {
  3003. Terminal->OutputBufferStart = 0;
  3004. }
  3005. Space += CopySize;
  3006. AnythingRead = TRUE;
  3007. BytesRead += CopySize;
  3008. }
  3009. Status = STATUS_SUCCESS;
  3010. TerminalMasterReadEnd:
  3011. if (AnythingRead != FALSE) {
  3012. if (LockHeld == FALSE) {
  3013. KeAcquireQueuedLock(Terminal->OutputLock);
  3014. LockHeld = TRUE;
  3015. }
  3016. IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, TRUE);
  3017. }
  3018. if (LockHeld != FALSE) {
  3019. KeReleaseQueuedLock(Terminal->OutputLock);
  3020. }
  3021. IoContext->BytesCompleted = BytesRead;
  3022. return Status;
  3023. }
  3024. KSTATUS
  3025. IopTerminalSlaveRead (
  3026. PFILE_OBJECT FileObject,
  3027. PIO_CONTEXT IoContext
  3028. )
  3029. /*++
  3030. Routine Description:
  3031. This routine reads data from the slave side (the slave's standard in).
  3032. Arguments:
  3033. FileObject - Supplies a pointer to the slave terminal file object.
  3034. IoContext - Supplies a pointer to the I/O context.
  3035. Return Value:
  3036. Status code. A failing status code does not necessarily mean no I/O made it
  3037. in or out. Check the bytes completed value in the I/O context to find out
  3038. how much occurred.
  3039. --*/
  3040. {
  3041. BOOL AcceptingSignal;
  3042. BOOL AnythingRead;
  3043. BOOL BreakForNewline;
  3044. UINTN BytesRead;
  3045. CHAR Character;
  3046. PCHAR ControlCharacters;
  3047. UINTN CopyIndex;
  3048. UINTN CopySize;
  3049. UCHAR FlushCount;
  3050. UCHAR FlushTime;
  3051. UINTN InputIndex;
  3052. ULONG LocalFlags;
  3053. BOOL LockHeld;
  3054. PIO_OBJECT_STATE MasterIoState;
  3055. PROCESS_GROUP_ID ProcessGroup;
  3056. ULONG ReturnedEvents;
  3057. SESSION_ID Session;
  3058. BOOL SignalProcessGroup;
  3059. PTERMINAL_SLAVE Slave;
  3060. PIO_OBJECT_STATE SlaveIoState;
  3061. ULONG Space;
  3062. KSTATUS Status;
  3063. PTERMINAL Terminal;
  3064. ULONG TimeoutInMilliseconds;
  3065. SignalProcessGroup = FALSE;
  3066. Slave = FileObject->SpecialIo;
  3067. ASSERT(Slave->Header.Type == ObjectTerminalSlave);
  3068. Terminal = Slave->Master;
  3069. ASSERT(FileObject == Terminal->SlaveFileObject);
  3070. SlaveIoState = Terminal->SlaveFileObject->IoState;
  3071. MasterIoState = Terminal->MasterFileObject->IoState;
  3072. LocalFlags = Terminal->Settings.LocalFlags;
  3073. ControlCharacters = Terminal->Settings.ControlCharacters;
  3074. TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
  3075. AnythingRead = FALSE;
  3076. BytesRead = 0;
  3077. LockHeld = FALSE;
  3078. PsGetProcessGroup(NULL, &ProcessGroup, &Session);
  3079. //
  3080. // Synchronize the checks on the terminal attachment and the owning session
  3081. // and process group with the IOCTLs that may modify them.
  3082. //
  3083. KeAcquireQueuedLock(Terminal->InputLock);
  3084. LockHeld = TRUE;
  3085. //
  3086. // If the reading process is not in the same process group, send the
  3087. // process group a signal unless the reading process is ignoring or
  3088. // blocking that signal.
  3089. //
  3090. if (ProcessGroup != Terminal->ProcessGroupId) {
  3091. //
  3092. // If it's an orphaned process, fail the I/O.
  3093. //
  3094. if (PsIsProcessGroupOrphaned(ProcessGroup) != FALSE) {
  3095. Status = STATUS_DEVICE_IO_ERROR;
  3096. goto TerminalSlaveReadEnd;
  3097. }
  3098. AcceptingSignal = PsIsThreadAcceptingSignal(
  3099. NULL,
  3100. SIGNAL_BACKGROUND_TERMINAL_INPUT);
  3101. //
  3102. // If the process is accepting that signal, send it to it and tell it
  3103. // to try again later. If it's not accepting it, just let it go through.
  3104. //
  3105. if (AcceptingSignal != FALSE) {
  3106. SignalProcessGroup = TRUE;
  3107. Status = STATUS_TRY_AGAIN;
  3108. goto TerminalSlaveReadEnd;
  3109. }
  3110. }
  3111. //
  3112. // Wait the designated amount of time, or block indefinitely.
  3113. //
  3114. if (TimeoutInMilliseconds == WAIT_TIME_INDEFINITE) {
  3115. FlushTime = ControlCharacters[TerminalCharacterFlushTime];
  3116. if (FlushTime != 0) {
  3117. TimeoutInMilliseconds = FlushTime * 100;
  3118. }
  3119. }
  3120. BreakForNewline = FALSE;
  3121. Status = STATUS_SUCCESS;
  3122. Space = IopTerminalGetInputBufferSpace(Terminal);
  3123. while (BytesRead < IoContext->SizeInBytes) {
  3124. //
  3125. // Wait for data to be ready.
  3126. //
  3127. if (Space == TERMINAL_INPUT_BUFFER_SIZE - 1) {
  3128. //
  3129. // In non-canonical mode, observe the minimum and timeout counts.
  3130. //
  3131. if ((LocalFlags & TERMINAL_LOCAL_CANONICAL) == 0) {
  3132. FlushCount = ControlCharacters[TerminalCharacterFlushCount];
  3133. if (FlushCount != 0) {
  3134. //
  3135. // If there's a minimum and it's been met, stop now.
  3136. //
  3137. if (BytesRead >= FlushCount) {
  3138. break;
  3139. }
  3140. //
  3141. // The minimum is zero. If time is also zero, then do not block.
  3142. //
  3143. } else {
  3144. if (ControlCharacters[TerminalCharacterFlushTime] == 0) {
  3145. TimeoutInMilliseconds = 0;
  3146. }
  3147. }
  3148. }
  3149. //
  3150. // If all open handles to the master were closed, there's never
  3151. // going to be any more data.
  3152. //
  3153. if (IO_IS_TERMINAL_MASTER_OPEN(Terminal) == FALSE) {
  3154. Status = STATUS_END_OF_FILE;
  3155. break;
  3156. }
  3157. IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, FALSE);
  3158. IoSetIoObjectState(MasterIoState, POLL_EVENT_OUT, TRUE);
  3159. KeReleaseQueuedLock(Terminal->InputLock);
  3160. LockHeld = FALSE;
  3161. Status = IoWaitForIoObjectState(SlaveIoState,
  3162. POLL_EVENT_IN,
  3163. TRUE,
  3164. TimeoutInMilliseconds,
  3165. &ReturnedEvents);
  3166. if (!KSUCCESS(Status)) {
  3167. goto TerminalSlaveReadEnd;
  3168. }
  3169. if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
  3170. Status = STATUS_DEVICE_IO_ERROR;
  3171. goto TerminalSlaveReadEnd;
  3172. }
  3173. KeAcquireQueuedLock(Terminal->InputLock);
  3174. LockHeld = TRUE;
  3175. Space = IopTerminalGetInputBufferSpace(Terminal);
  3176. if (Space == TERMINAL_INPUT_BUFFER_SIZE - 1) {
  3177. break;
  3178. }
  3179. }
  3180. //
  3181. // Determine how much to copy out of the terminal's input buffer.
  3182. //
  3183. CopySize = (TERMINAL_INPUT_BUFFER_SIZE - 1) - Space;
  3184. if (CopySize > IoContext->SizeInBytes - BytesRead) {
  3185. CopySize = IoContext->SizeInBytes - BytesRead;
  3186. }
  3187. if (CopySize >
  3188. TERMINAL_INPUT_BUFFER_SIZE - Terminal->InputBufferStart) {
  3189. CopySize = TERMINAL_INPUT_BUFFER_SIZE - Terminal->InputBufferStart;
  3190. }
  3191. //
  3192. // If it's canonical, look for a newline and break on that.
  3193. //
  3194. if ((LocalFlags & TERMINAL_LOCAL_CANONICAL) != 0) {
  3195. for (CopyIndex = 0; CopyIndex < CopySize; CopyIndex += 1) {
  3196. InputIndex = Terminal->InputBufferStart + CopyIndex;
  3197. Character = Terminal->InputBuffer[InputIndex];
  3198. if ((Character ==
  3199. ControlCharacters[TerminalCharacterEndOfLine]) ||
  3200. (Character == '\n')) {
  3201. CopySize = CopyIndex + 1;
  3202. BreakForNewline = TRUE;
  3203. break;
  3204. }
  3205. }
  3206. }
  3207. Status = MmCopyIoBufferData(
  3208. IoContext->IoBuffer,
  3209. Terminal->InputBuffer + Terminal->InputBufferStart,
  3210. BytesRead,
  3211. CopySize,
  3212. TRUE);
  3213. if (!KSUCCESS(Status)) {
  3214. break;
  3215. }
  3216. Terminal->InputBufferStart += CopySize;
  3217. ASSERT(Terminal->InputBufferStart <= TERMINAL_INPUT_BUFFER_SIZE);
  3218. if (Terminal->InputBufferStart == TERMINAL_INPUT_BUFFER_SIZE) {
  3219. Terminal->InputBufferStart = 0;
  3220. }
  3221. BytesRead += CopySize;
  3222. Space += CopySize;
  3223. AnythingRead = TRUE;
  3224. //
  3225. // If this was a newline and it's canonical mode, then let the user
  3226. // chew on that.
  3227. //
  3228. if (BreakForNewline != FALSE) {
  3229. break;
  3230. }
  3231. }
  3232. ASSERT(LockHeld != FALSE);
  3233. //
  3234. // Unsignal the input event if this routine read the last of the available
  3235. // data.
  3236. //
  3237. if ((AnythingRead != FALSE) && (Space == TERMINAL_INPUT_BUFFER_SIZE - 1)) {
  3238. IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, FALSE);
  3239. IoSetIoObjectState(MasterIoState, POLL_EVENT_OUT, TRUE);
  3240. }
  3241. TerminalSlaveReadEnd:
  3242. if (AnythingRead != FALSE) {
  3243. if (LockHeld == FALSE) {
  3244. KeAcquireQueuedLock(Terminal->InputLock);
  3245. LockHeld = TRUE;
  3246. }
  3247. IoSetIoObjectState(MasterIoState, POLL_EVENT_OUT, TRUE);
  3248. }
  3249. if (LockHeld != FALSE) {
  3250. KeReleaseQueuedLock(Terminal->InputLock);
  3251. }
  3252. if (SignalProcessGroup != FALSE) {
  3253. PsSignalProcessGroup(ProcessGroup, SIGNAL_BACKGROUND_TERMINAL_INPUT);
  3254. }
  3255. IoContext->BytesCompleted = BytesRead;
  3256. return Status;
  3257. }
  3258. KSTATUS
  3259. IopTerminalWriteOutputBuffer (
  3260. PTERMINAL Terminal,
  3261. PVOID Buffer,
  3262. UINTN SizeInBytes,
  3263. ULONG RepeatCount,
  3264. ULONG TimeoutInMilliseconds
  3265. )
  3266. /*++
  3267. Routine Description:
  3268. This routine writes data to the terminal output buffer. It assumes the
  3269. output lock is already held and it does not set any events. It may
  3270. release and reacquire the output lock during the course of the routine, but
  3271. the routine will always return with the output lock held (just like it
  3272. started with).
  3273. Arguments:
  3274. Terminal - Supplies a pointer to the terminal.
  3275. Buffer - Supplies a pointer to the buffer that contains the data to write.
  3276. SizeInBytes - Supplies the number of bytes to write.
  3277. RepeatCount - Supplies the number of times to write the buffer to the
  3278. output.
  3279. TimeoutInMilliseconds - Supplies the number of milliseconds that the I/O
  3280. operation should be waited on before timing out. Use
  3281. WAIT_TIME_INDEFINITE to wait forever on the I/O.
  3282. Return Value:
  3283. Status code.
  3284. --*/
  3285. {
  3286. UCHAR Byte;
  3287. PUCHAR ByteBuffer;
  3288. UINTN ByteIndex;
  3289. BOOL DidLeadingCharacter;
  3290. BOOL LockHeld;
  3291. ULONG Mask;
  3292. PIO_OBJECT_STATE MasterIoState;
  3293. ULONG OutputFlags;
  3294. ULONG RepeatIndex;
  3295. ULONG ReturnedEvents;
  3296. PIO_OBJECT_STATE SlaveIoState;
  3297. ULONG Space;
  3298. KSTATUS Status;
  3299. ByteBuffer = (PUCHAR)Buffer;
  3300. DidLeadingCharacter = FALSE;
  3301. LockHeld = TRUE;
  3302. OutputFlags = Terminal->Settings.OutputFlags;
  3303. MasterIoState = Terminal->MasterFileObject->IoState;
  3304. SlaveIoState = Terminal->SlaveFileObject->IoState;
  3305. Space = IopTerminalGetOutputBufferSpace(Terminal);
  3306. for (RepeatIndex = 0; RepeatIndex < RepeatCount; RepeatIndex += 1) {
  3307. for (ByteIndex = 0; ByteIndex < SizeInBytes; ByteIndex += 1) {
  3308. //
  3309. // Wait for space to become available.
  3310. //
  3311. if ((Space == 0) && (Terminal->HardwareHandle != NULL)) {
  3312. Status = IopTerminalFlushOutputToDevice(Terminal);
  3313. if (!KSUCCESS(Status)) {
  3314. goto TerminalWriteOutputBufferEnd;
  3315. }
  3316. Space = IopTerminalGetOutputBufferSpace(Terminal);
  3317. ASSERT(Space != 0);
  3318. }
  3319. while (Space == 0) {
  3320. IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, TRUE);
  3321. IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, FALSE);
  3322. KeReleaseQueuedLock(Terminal->OutputLock);
  3323. LockHeld = FALSE;
  3324. Status = IoWaitForIoObjectState(SlaveIoState,
  3325. POLL_EVENT_OUT,
  3326. TRUE,
  3327. TimeoutInMilliseconds,
  3328. &ReturnedEvents);
  3329. if (!KSUCCESS(Status)) {
  3330. goto TerminalWriteOutputBufferEnd;
  3331. }
  3332. if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
  3333. Status = STATUS_DEVICE_IO_ERROR;
  3334. goto TerminalWriteOutputBufferEnd;
  3335. }
  3336. KeAcquireQueuedLock(Terminal->OutputLock);
  3337. LockHeld = TRUE;
  3338. Space = IopTerminalGetOutputBufferSpace(Terminal);
  3339. }
  3340. //
  3341. // Process any output flags.
  3342. //
  3343. Byte = ByteBuffer[ByteIndex];
  3344. if (Byte == '\r') {
  3345. if ((OutputFlags & TERMINAL_OUTPUT_CR_TO_NEWLINE) != 0) {
  3346. Byte = '\n';
  3347. }
  3348. } else if (Byte == '\n') {
  3349. //
  3350. // If \n should be translated to \r\n, then change the byte to
  3351. // \r, and decrement the loop counter to go around again on the
  3352. // same byte. The second time around, just output the \n.
  3353. //
  3354. Mask = TERMINAL_OUTPUT_POST_PROCESS |
  3355. TERMINAL_OUTPUT_NEWLINE_TO_CRLF;
  3356. if ((OutputFlags & Mask) == Mask) {
  3357. if (DidLeadingCharacter == FALSE) {
  3358. Byte = '\r';
  3359. ByteIndex -= 1;
  3360. DidLeadingCharacter = TRUE;
  3361. } else {
  3362. DidLeadingCharacter = FALSE;
  3363. }
  3364. }
  3365. }
  3366. //
  3367. // Write the byte in.
  3368. //
  3369. Terminal->OutputBuffer[Terminal->OutputBufferEnd] = Byte;
  3370. Terminal->OutputBufferEnd += 1;
  3371. if (Terminal->OutputBufferEnd == TERMINAL_OUTPUT_BUFFER_SIZE) {
  3372. Terminal->OutputBufferEnd = 0;
  3373. }
  3374. Space -= 1;
  3375. }
  3376. }
  3377. if (Terminal->HardwareHandle != NULL) {
  3378. ASSERT(LockHeld != FALSE);
  3379. Status = IopTerminalFlushOutputToDevice(Terminal);
  3380. if (!KSUCCESS(Status)) {
  3381. goto TerminalWriteOutputBufferEnd;
  3382. }
  3383. }
  3384. Status = STATUS_SUCCESS;
  3385. TerminalWriteOutputBufferEnd:
  3386. if (LockHeld == FALSE) {
  3387. KeAcquireQueuedLock(Terminal->OutputLock);
  3388. }
  3389. return Status;
  3390. }
  3391. ULONG
  3392. IopTerminalGetInputBufferSpace (
  3393. PTERMINAL Terminal
  3394. )
  3395. /*++
  3396. Routine Description:
  3397. This routine returns the amount of space available in bytes in the input
  3398. buffer of a terminal.
  3399. Arguments:
  3400. Terminal - Supplies a pointer to the terminal.
  3401. Return Value:
  3402. returns the number of bytes available in the input buffer.
  3403. --*/
  3404. {
  3405. ULONG Space;
  3406. if (Terminal->InputBufferEnd >= Terminal->InputBufferStart) {
  3407. Space = TERMINAL_INPUT_BUFFER_SIZE - 1 -
  3408. (Terminal->InputBufferEnd - Terminal->InputBufferStart);
  3409. //
  3410. // The buffer has wrapped around.
  3411. //
  3412. } else {
  3413. Space = Terminal->InputBufferStart - Terminal->InputBufferEnd - 1;
  3414. }
  3415. return Space;
  3416. }
  3417. ULONG
  3418. IopTerminalGetOutputBufferSpace (
  3419. PTERMINAL Terminal
  3420. )
  3421. /*++
  3422. Routine Description:
  3423. This routine returns the amount of space available in bytes in the output
  3424. buffer of a terminal.
  3425. Arguments:
  3426. Terminal - Supplies a pointer to the terminal.
  3427. Return Value:
  3428. returns the number of bytes available in the output buffer.
  3429. --*/
  3430. {
  3431. ULONG Space;
  3432. if (Terminal->OutputBufferEnd >= Terminal->OutputBufferStart) {
  3433. Space = TERMINAL_OUTPUT_BUFFER_SIZE - 1 -
  3434. (Terminal->OutputBufferEnd - Terminal->OutputBufferStart);
  3435. //
  3436. // The buffer has wrapped around.
  3437. //
  3438. } else {
  3439. Space = Terminal->OutputBufferStart - Terminal->OutputBufferEnd - 1;
  3440. }
  3441. return Space;
  3442. }
  3443. KSTATUS
  3444. IopTerminalFixUpCanonicalLine (
  3445. PTERMINAL Terminal,
  3446. ULONG TimeoutInMilliseconds,
  3447. ULONG DirtyRegionBegin,
  3448. ULONG DirtyRegionEnd,
  3449. ULONG CurrentScreenPosition
  3450. )
  3451. /*++
  3452. Routine Description:
  3453. This routine fixes up the terminal output for canonical mode processing
  3454. either when a block of input or a valid line is finished. It does not
  3455. acquire any locks or set any events, it assumes that is handled by the
  3456. caller. Specifically, the working input lock and output lock must both
  3457. be held.
  3458. Arguments:
  3459. Terminal - Supplies a pointer to the terminal.
  3460. TimeoutInMilliseconds - Supplies the amount of time to wait for output
  3461. operations before giving up.
  3462. DirtyRegionBegin - Supplies the first character in the dirty region, as an
  3463. offset in characters from the beginning of the line. The beginning of
  3464. the line may not be column zero.
  3465. DirtyRegionEnd - Supplies the first character not in the dirty region, as
  3466. an offset in characters from the beginning of the line.
  3467. CurrentScreenPosition - Supplies the position of the screen's cursor as an
  3468. offset in characters from the beginning of the line.
  3469. Return Value:
  3470. Status code.
  3471. --*/
  3472. {
  3473. CHAR Character;
  3474. PCHAR ControlCharacters;
  3475. KSTATUS Status;
  3476. ULONG ValidLineEnd;
  3477. ULONG WorkingInputLength;
  3478. ControlCharacters = Terminal->Settings.ControlCharacters;
  3479. //
  3480. // If the last character is a newline, pretend it's not there.
  3481. //
  3482. WorkingInputLength = Terminal->WorkingInputLength;
  3483. if ((WorkingInputLength != 0) &&
  3484. ((Terminal->WorkingInputBuffer[WorkingInputLength - 1] ==
  3485. ControlCharacters[TerminalCharacterEndOfLine]) ||
  3486. (Terminal->WorkingInputBuffer[WorkingInputLength - 1] == '\n'))) {
  3487. WorkingInputLength -= 1;
  3488. }
  3489. //
  3490. // Back up to the start of the dirty region.
  3491. //
  3492. ASSERT(DirtyRegionBegin <= CurrentScreenPosition);
  3493. if (DirtyRegionBegin < CurrentScreenPosition) {
  3494. Character = '\b';
  3495. Status = IopTerminalWriteOutputBuffer(
  3496. Terminal,
  3497. &Character,
  3498. 1,
  3499. CurrentScreenPosition - DirtyRegionBegin,
  3500. TimeoutInMilliseconds);
  3501. if (!KSUCCESS(Status)) {
  3502. goto TerminalFixUpCanonicalLineEnd;
  3503. }
  3504. CurrentScreenPosition = DirtyRegionBegin;
  3505. }
  3506. //
  3507. // Write out the portion of the dirty region that's still a valid line.
  3508. //
  3509. ValidLineEnd = DirtyRegionEnd;
  3510. if (WorkingInputLength < ValidLineEnd) {
  3511. ValidLineEnd = WorkingInputLength;
  3512. }
  3513. if (ValidLineEnd > DirtyRegionBegin) {
  3514. Status = IopTerminalWriteOutputBuffer(
  3515. Terminal,
  3516. Terminal->WorkingInputBuffer + DirtyRegionBegin,
  3517. ValidLineEnd - DirtyRegionBegin,
  3518. 1,
  3519. TimeoutInMilliseconds);
  3520. if (!KSUCCESS(Status)) {
  3521. goto TerminalFixUpCanonicalLineEnd;
  3522. }
  3523. CurrentScreenPosition += ValidLineEnd - DirtyRegionBegin;
  3524. }
  3525. //
  3526. // Write spaces to erase any additional portion that goes beyond the valid
  3527. // line end.
  3528. //
  3529. if (CurrentScreenPosition < DirtyRegionEnd) {
  3530. Character = ' ';
  3531. Status = IopTerminalWriteOutputBuffer(
  3532. Terminal,
  3533. &Character,
  3534. 1,
  3535. DirtyRegionEnd - CurrentScreenPosition,
  3536. TimeoutInMilliseconds);
  3537. if (!KSUCCESS(Status)) {
  3538. goto TerminalFixUpCanonicalLineEnd;
  3539. }
  3540. CurrentScreenPosition = DirtyRegionEnd;
  3541. }
  3542. //
  3543. // Finally, back up to the cursor position.
  3544. //
  3545. if (CurrentScreenPosition > Terminal->WorkingInputCursor) {
  3546. Character = '\b';
  3547. Status = IopTerminalWriteOutputBuffer(
  3548. Terminal,
  3549. &Character,
  3550. 1,
  3551. CurrentScreenPosition - Terminal->WorkingInputCursor,
  3552. TimeoutInMilliseconds);
  3553. if (!KSUCCESS(Status)) {
  3554. goto TerminalFixUpCanonicalLineEnd;
  3555. }
  3556. }
  3557. Status = STATUS_SUCCESS;
  3558. TerminalFixUpCanonicalLineEnd:
  3559. return Status;
  3560. }
  3561. BOOL
  3562. IopTerminalProcessEditingCharacter (
  3563. PTERMINAL Terminal,
  3564. CHAR Character,
  3565. ULONG TimeoutInMilliseconds,
  3566. PULONG DirtyRegionBegin,
  3567. PULONG DirtyRegionEnd,
  3568. PULONG ScreenCursorPosition,
  3569. PBOOL OutputWritten
  3570. )
  3571. /*++
  3572. Routine Description:
  3573. This routine processes any characters that change the working buffer in a
  3574. non-straightforward way. This routine operates in canonical mode only.
  3575. Arguments:
  3576. Terminal - Supplies a pointer to the terminal.
  3577. Character - Supplies the character to process.
  3578. TimeoutInMilliseconds - Supplies the number of milliseconds to wait if the
  3579. output buffer is written to.
  3580. DirtyRegionBegin - Supplies a pointer that contains the region of the line
  3581. that needs to be redrawn. This may get expanded on output.
  3582. DirtyRegionEnd - Supplies a pointer that contains the end of the region of
  3583. the line that needs to be redrawn. This also may get expanded on
  3584. output.
  3585. ScreenCursorPosition - Supplies a pointer that contains the screen's
  3586. current cursor position on input, and may get altered on output.
  3587. OutputWritten - Supplies a pointer where TRUE will be returned if the
  3588. output buffer was written to. Otherwise, this value will be left
  3589. untouched.
  3590. Return Value:
  3591. TRUE if the byte was handled by this routine and should not be added to the
  3592. working buffer.
  3593. FALSE if the character was not handled by this routine.
  3594. --*/
  3595. {
  3596. TERMINAL_COMMAND_DATA CommandData;
  3597. PCHAR ControlCharacters;
  3598. PTERMINAL_HISTORY_ENTRY HistoryEntry;
  3599. UINTN LastIndex;
  3600. ULONG LocalFlags;
  3601. UINTN MoveIndex;
  3602. PLIST_ENTRY NextListEntry;
  3603. CHAR OutputString[TERMINAL_MAX_CANONICAL_OUTPUT];
  3604. UINTN OutputStringLength;
  3605. TERMINAL_PARSE_RESULT ParseResult;
  3606. BOOL Result;
  3607. ControlCharacters = Terminal->Settings.ControlCharacters;
  3608. LocalFlags = Terminal->Settings.LocalFlags;
  3609. ParseResult = TermProcessInput(&(Terminal->KeyData), Character);
  3610. switch (ParseResult) {
  3611. case TerminalParseResultNormalCharacter:
  3612. //
  3613. // Erase backs up one.
  3614. //
  3615. Result = FALSE;
  3616. if (Character == ControlCharacters[TerminalCharacterErase]) {
  3617. if (Terminal->WorkingInputCursor != 0) {
  3618. Terminal->WorkingInputCursor -= 1;
  3619. ASSERT(Terminal->WorkingInputLength != 0);
  3620. //
  3621. // Potentially expand the portion of the screen that will
  3622. // need cleaning up.
  3623. //
  3624. if ((LocalFlags & TERMINAL_LOCAL_ECHO_ERASE) != 0) {
  3625. if (Terminal->WorkingInputCursor < *DirtyRegionBegin) {
  3626. *DirtyRegionBegin = Terminal->WorkingInputCursor;
  3627. }
  3628. if (Terminal->WorkingInputLength + 1 > *DirtyRegionEnd) {
  3629. *DirtyRegionEnd = Terminal->WorkingInputLength + 1;
  3630. }
  3631. //
  3632. // If not echoing erase, print the character that was just
  3633. // erased to indicate what happened. This is useful for
  3634. // line printers.
  3635. //
  3636. } else {
  3637. LastIndex = Terminal->WorkingInputCursor + 1;
  3638. OutputString[0] = Terminal->WorkingInputBuffer[LastIndex];
  3639. IopTerminalWriteOutputBuffer(Terminal,
  3640. OutputString,
  3641. 1,
  3642. 1,
  3643. TimeoutInMilliseconds);
  3644. }
  3645. //
  3646. // Move the characters after the cursor back one.
  3647. //
  3648. for (MoveIndex = Terminal->WorkingInputCursor;
  3649. MoveIndex < Terminal->WorkingInputLength - 1;
  3650. MoveIndex += 1) {
  3651. Terminal->WorkingInputBuffer[MoveIndex] =
  3652. Terminal->WorkingInputBuffer[MoveIndex + 1];
  3653. }
  3654. Terminal->WorkingInputLength -= 1;
  3655. }
  3656. Result = TRUE;
  3657. //
  3658. // Kill erases the whole line.
  3659. //
  3660. } else if (Character == ControlCharacters[TerminalCharacterKill]) {
  3661. //
  3662. // If the extended bit is set, visually erase the whole line.
  3663. //
  3664. if ((LocalFlags & TERMINAL_LOCAL_ECHO_KILL_EXTENDED) != 0) {
  3665. Result = TRUE;
  3666. *DirtyRegionBegin = 0;
  3667. if (Terminal->WorkingInputLength > *DirtyRegionEnd) {
  3668. *DirtyRegionEnd = Terminal->WorkingInputLength;
  3669. }
  3670. //
  3671. // Otherwise if the old echo kill is set, add a newline.
  3672. //
  3673. } else if ((LocalFlags & TERMINAL_LOCAL_ECHO_KILL_NEWLINE) != 0) {
  3674. Result = TRUE;
  3675. OutputString[0] = Character;
  3676. OutputString[1] = '\n';
  3677. IopTerminalWriteOutputBuffer(Terminal,
  3678. OutputString,
  3679. 2,
  3680. 1,
  3681. TimeoutInMilliseconds);
  3682. //
  3683. // Just echo the kill character.
  3684. //
  3685. } else {
  3686. Result = FALSE;
  3687. }
  3688. Terminal->WorkingInputCursor = 0;
  3689. Terminal->WorkingInputLength = 0;
  3690. Terminal->Flags &= ~(TERMINAL_FLAG_VIRGIN_LINE |
  3691. TERMINAL_FLAG_UNEDITED_LINE);
  3692. //
  3693. // These other characters are simply not printed.
  3694. //
  3695. } else {
  3696. if ((Character == ControlCharacters[TerminalCharacterStart]) ||
  3697. (Character == ControlCharacters[TerminalCharacterStop])) {
  3698. Result = TRUE;
  3699. }
  3700. }
  3701. return Result;
  3702. case TerminalParseResultPartialCommand:
  3703. return TRUE;
  3704. case TerminalParseResultCompleteCommand:
  3705. break;
  3706. default:
  3707. ASSERT(FALSE);
  3708. return FALSE;
  3709. }
  3710. //
  3711. // Handle the complete key that just came in.
  3712. //
  3713. RtlZeroMemory(&CommandData, sizeof(TERMINAL_COMMAND_DATA));
  3714. switch (Terminal->KeyData.Key) {
  3715. case TerminalKeyPageUp:
  3716. case TerminalKeyPageDown:
  3717. if (Terminal->KeyData.Key == TerminalKeyPageUp) {
  3718. CommandData.Command = TerminalCommandScrollUp;
  3719. } else {
  3720. CommandData.Command = TerminalCommandScrollDown;
  3721. }
  3722. CommandData.ParameterCount = 1;
  3723. CommandData.Parameter[0] = TERMINAL_SCROLL_LINE_COUNT;
  3724. Result = TermCreateOutputSequence(&CommandData,
  3725. OutputString,
  3726. sizeof(OutputString));
  3727. if (Result != FALSE) {
  3728. OutputString[sizeof(OutputString) - 1] = '\0';
  3729. OutputStringLength = RtlStringLength(OutputString);
  3730. IopTerminalWriteOutputBuffer(Terminal,
  3731. OutputString,
  3732. OutputStringLength,
  3733. 1,
  3734. TimeoutInMilliseconds);
  3735. *OutputWritten = TRUE;
  3736. }
  3737. break;
  3738. case TerminalKeyUp:
  3739. case TerminalKeyDown:
  3740. //
  3741. // If the list is empty, there's nothing to do.
  3742. //
  3743. if (LIST_EMPTY(&(Terminal->CommandHistory)) != FALSE) {
  3744. break;
  3745. }
  3746. if (Terminal->LastCommand != NULL) {
  3747. NextListEntry = &(Terminal->LastCommand->ListEntry);
  3748. } else {
  3749. NextListEntry = &(Terminal->CommandHistory);
  3750. }
  3751. if (Terminal->KeyData.Key == TerminalKeyUp) {
  3752. //
  3753. // If it's a virgin line, use the one currently
  3754. // pointed at (the real last command). Otherwise, go
  3755. // up one.
  3756. //
  3757. if ((Terminal->Flags & TERMINAL_FLAG_VIRGIN_LINE) == 0) {
  3758. NextListEntry = NextListEntry->Next;
  3759. }
  3760. if (NextListEntry == &(Terminal->CommandHistory)) {
  3761. NextListEntry = NextListEntry->Next;
  3762. }
  3763. //
  3764. // Move to the next command.
  3765. //
  3766. } else {
  3767. NextListEntry = NextListEntry->Previous;
  3768. if (NextListEntry == &(Terminal->CommandHistory)) {
  3769. NextListEntry = NextListEntry->Previous;
  3770. }
  3771. }
  3772. ASSERT(NextListEntry != &(Terminal->CommandHistory));
  3773. HistoryEntry = LIST_VALUE(NextListEntry,
  3774. TERMINAL_HISTORY_ENTRY,
  3775. ListEntry);
  3776. //
  3777. // Mark the whole current command region as dirty.
  3778. //
  3779. *DirtyRegionBegin = 0;
  3780. if (Terminal->WorkingInputLength > *DirtyRegionEnd) {
  3781. *DirtyRegionEnd = Terminal->WorkingInputLength;
  3782. }
  3783. //
  3784. // Copy in the new command.
  3785. //
  3786. ASSERT(HistoryEntry->CommandLength < TERMINAL_CANONICAL_BUFFER_SIZE);
  3787. RtlCopyMemory(Terminal->WorkingInputBuffer,
  3788. HistoryEntry->Command,
  3789. HistoryEntry->CommandLength);
  3790. Terminal->WorkingInputLength = HistoryEntry->CommandLength;
  3791. Terminal->WorkingInputCursor = HistoryEntry->CommandLength;
  3792. if (Terminal->WorkingInputLength > *DirtyRegionEnd) {
  3793. *DirtyRegionEnd = Terminal->WorkingInputLength;
  3794. }
  3795. Terminal->LastCommand = HistoryEntry;
  3796. Terminal->Flags &= ~TERMINAL_FLAG_VIRGIN_LINE;
  3797. Terminal->Flags |= TERMINAL_FLAG_UNEDITED_LINE;
  3798. break;
  3799. case TerminalKeyRight:
  3800. if (Terminal->WorkingInputCursor != Terminal->WorkingInputLength) {
  3801. Terminal->WorkingInputCursor += 1;
  3802. if (Terminal->WorkingInputCursor > *DirtyRegionEnd) {
  3803. *DirtyRegionEnd = Terminal->WorkingInputCursor;
  3804. }
  3805. }
  3806. break;
  3807. case TerminalKeyLeft:
  3808. if (Terminal->WorkingInputCursor != 0) {
  3809. Terminal->WorkingInputCursor -= 1;
  3810. if (Terminal->WorkingInputCursor < *DirtyRegionBegin) {
  3811. *DirtyRegionBegin = Terminal->WorkingInputCursor;
  3812. }
  3813. }
  3814. break;
  3815. default:
  3816. break;
  3817. }
  3818. return TRUE;
  3819. }
  3820. VOID
  3821. IopTerminalAddHistoryEntry (
  3822. PTERMINAL Terminal
  3823. )
  3824. /*++
  3825. Routine Description:
  3826. This routine processes a new command in canonical mode, adding it to the
  3827. command history list.
  3828. Arguments:
  3829. Terminal - Supplies a pointer to the terminal.
  3830. Return Value:
  3831. None. The routine may fail, but it's largely inconsequential.
  3832. --*/
  3833. {
  3834. ULONG AllocationSize;
  3835. ULONG CommandLength;
  3836. PCHAR ControlCharacters;
  3837. PTERMINAL_HISTORY_ENTRY HistoryEntry;
  3838. HistoryEntry = NULL;
  3839. CommandLength = Terminal->WorkingInputLength;
  3840. ControlCharacters = Terminal->Settings.ControlCharacters;
  3841. if ((CommandLength != 0) &&
  3842. ((Terminal->WorkingInputBuffer[CommandLength - 1] ==
  3843. ControlCharacters[TerminalCharacterEndOfLine]) ||
  3844. (Terminal->WorkingInputBuffer[CommandLength - 1] == '\n'))) {
  3845. CommandLength -= 1;
  3846. }
  3847. if (CommandLength == 0) {
  3848. return;
  3849. }
  3850. if (TERMINAL_MAX_COMMAND_HISTORY == 0) {
  3851. return;
  3852. }
  3853. //
  3854. // If it came straight from the command history, don't add enother entry.
  3855. //
  3856. if ((Terminal->Flags & TERMINAL_FLAG_UNEDITED_LINE) != 0) {
  3857. return;
  3858. }
  3859. //
  3860. // Calculate the size needed for the new entry.
  3861. //
  3862. AllocationSize = sizeof(TERMINAL_HISTORY_ENTRY) +
  3863. (sizeof(CHAR) * (CommandLength - ANYSIZE_ARRAY));
  3864. //
  3865. // Remove the last history entry if the list is full.
  3866. //
  3867. if (Terminal->CommandHistorySize >= TERMINAL_MAX_COMMAND_HISTORY) {
  3868. ASSERT(Terminal->CommandHistory.Previous !=
  3869. &(Terminal->CommandHistory));
  3870. HistoryEntry = LIST_VALUE(Terminal->CommandHistory.Previous,
  3871. TERMINAL_HISTORY_ENTRY,
  3872. ListEntry);
  3873. if (Terminal->LastCommand == HistoryEntry) {
  3874. ASSERT(HistoryEntry->ListEntry.Previous !=
  3875. &(Terminal->CommandHistory));
  3876. Terminal->LastCommand = LIST_VALUE(HistoryEntry->ListEntry.Previous,
  3877. TERMINAL_HISTORY_ENTRY,
  3878. ListEntry);
  3879. }
  3880. LIST_REMOVE(&(HistoryEntry->ListEntry));
  3881. Terminal->CommandHistorySize -= 1;
  3882. //
  3883. // If the entry cannot be reused, then free it.
  3884. //
  3885. if (HistoryEntry->CommandLength < CommandLength) {
  3886. MmFreePagedPool(HistoryEntry);
  3887. HistoryEntry = NULL;
  3888. }
  3889. }
  3890. //
  3891. // Allocate a new entry if one is not being reused.
  3892. //
  3893. if (HistoryEntry == NULL) {
  3894. HistoryEntry = MmAllocatePagedPool(AllocationSize,
  3895. TERMINAL_ALLOCATION_TAG);
  3896. if (HistoryEntry == NULL) {
  3897. return;
  3898. }
  3899. }
  3900. //
  3901. // Initialize the entry and add it to the list.
  3902. //
  3903. RtlCopyMemory(HistoryEntry->Command,
  3904. Terminal->WorkingInputBuffer,
  3905. CommandLength);
  3906. HistoryEntry->CommandLength = CommandLength;
  3907. INSERT_AFTER(&(HistoryEntry->ListEntry), &(Terminal->CommandHistory));
  3908. Terminal->CommandHistorySize += 1;
  3909. Terminal->LastCommand = HistoryEntry;
  3910. return;
  3911. }
  3912. KSTATUS
  3913. IopTerminalUserBufferCopy (
  3914. BOOL FromKernelMode,
  3915. BOOL FromBuffer,
  3916. PVOID UserBuffer,
  3917. PVOID LocalBuffer,
  3918. UINTN Size
  3919. )
  3920. /*++
  3921. Routine Description:
  3922. This routine copies to or from a user mode or kernel mode buffer.
  3923. Arguments:
  3924. FromKernelMode - Supplies a boolean indicating whether or not this request
  3925. (and the buffer associated with it) originates from user mode (FALSE)
  3926. or kernel mode (TRUE).
  3927. FromBuffer - Supplies a boolean indicating whether to copy to the user
  3928. buffer (FALSE) or from the user buffer (TRUE).
  3929. UserBuffer - Supplies the user buffer pointer.
  3930. LocalBuffer - Supplies the local kernel mode buffer.
  3931. Size - Supplies the number of bytes to copy.
  3932. Return Value:
  3933. Status code.
  3934. --*/
  3935. {
  3936. KSTATUS Status;
  3937. //
  3938. // If the caller says it's from kernel mode, it better be a kernel mode
  3939. // address.
  3940. //
  3941. ASSERT((UserBuffer >= KERNEL_VA_START) || (FromKernelMode == FALSE));
  3942. Status = STATUS_SUCCESS;
  3943. if (FromBuffer != FALSE) {
  3944. if (FromKernelMode != FALSE) {
  3945. RtlCopyMemory(LocalBuffer, UserBuffer, Size);
  3946. } else {
  3947. Status = MmCopyFromUserMode(LocalBuffer, UserBuffer, Size);
  3948. }
  3949. } else {
  3950. if (FromKernelMode != FALSE) {
  3951. RtlCopyMemory(UserBuffer, LocalBuffer, Size);
  3952. } else {
  3953. Status = MmCopyToUserMode(UserBuffer, LocalBuffer, Size);
  3954. }
  3955. }
  3956. return Status;
  3957. }
  3958. KSTATUS
  3959. IopTerminalFlushOutputToDevice (
  3960. PTERMINAL Terminal
  3961. )
  3962. /*++
  3963. Routine Description:
  3964. This routine writes the currently buffered output data to the hardware
  3965. device. This routine assumes the output lock is already held.
  3966. Arguments:
  3967. Terminal - Supplies a pointer to the terminal whose output data should be
  3968. flushed.
  3969. Return Value:
  3970. Status code.
  3971. --*/
  3972. {
  3973. UINTN BytesWritten;
  3974. IO_BUFFER IoBuffer;
  3975. UINTN Size;
  3976. KSTATUS Status;
  3977. ASSERT(Terminal->HardwareHandle != NULL);
  3978. Status = STATUS_SUCCESS;
  3979. //
  3980. // Loop writing portions of the output buffer.
  3981. //
  3982. while (TRUE) {
  3983. //
  3984. // If the start is greater than the end, then the buffer has wrapped
  3985. // around and needs to be written in two steps.
  3986. //
  3987. if (Terminal->OutputBufferEnd < Terminal->OutputBufferStart) {
  3988. Size = TERMINAL_OUTPUT_BUFFER_SIZE - Terminal->OutputBufferStart;
  3989. Status = MmInitializeIoBuffer(
  3990. &IoBuffer,
  3991. Terminal->OutputBuffer + Terminal->OutputBufferStart,
  3992. INVALID_PHYSICAL_ADDRESS,
  3993. Size,
  3994. IO_BUFFER_FLAG_KERNEL_MODE_DATA);
  3995. if (!KSUCCESS(Status)) {
  3996. return Status;
  3997. }
  3998. Status = IoWrite(Terminal->HardwareHandle,
  3999. &IoBuffer,
  4000. Size,
  4001. 0,
  4002. WAIT_TIME_INDEFINITE,
  4003. &BytesWritten);
  4004. if (!KSUCCESS(Status)) {
  4005. return Status;
  4006. }
  4007. Terminal->OutputBufferStart += BytesWritten;
  4008. if (Terminal->OutputBufferStart == TERMINAL_OUTPUT_BUFFER_SIZE) {
  4009. Terminal->OutputBufferStart = 0;
  4010. }
  4011. continue;
  4012. }
  4013. //
  4014. // If the buffer is empty, stop.
  4015. //
  4016. if (Terminal->OutputBufferStart == Terminal->OutputBufferEnd) {
  4017. break;
  4018. }
  4019. Size = Terminal->OutputBufferEnd - Terminal->OutputBufferStart;
  4020. Status = MmInitializeIoBuffer(
  4021. &IoBuffer,
  4022. Terminal->OutputBuffer + Terminal->OutputBufferStart,
  4023. INVALID_PHYSICAL_ADDRESS,
  4024. Size,
  4025. IO_BUFFER_FLAG_KERNEL_MODE_DATA);
  4026. if (!KSUCCESS(Status)) {
  4027. return Status;
  4028. }
  4029. Status = IoWrite(Terminal->HardwareHandle,
  4030. &IoBuffer,
  4031. Size,
  4032. 0,
  4033. WAIT_TIME_INDEFINITE,
  4034. &BytesWritten);
  4035. if (!KSUCCESS(Status)) {
  4036. return Status;
  4037. }
  4038. Terminal->OutputBufferStart += BytesWritten;
  4039. ASSERT(Terminal->OutputBufferStart != TERMINAL_OUTPUT_BUFFER_SIZE);
  4040. }
  4041. return Status;
  4042. }
  4043. PTERMINAL
  4044. IopLookupTerminal (
  4045. SESSION_ID SessionId
  4046. )
  4047. /*++
  4048. Routine Description:
  4049. This routine attempts to find the controlling terminal of the given
  4050. session. This routine assumes that the terminal list lock is held and does
  4051. not take a reference on the terminal.
  4052. Arguments:
  4053. SessionId - Supplies the ID of the session whose controlling terminal is to
  4054. be looked up.
  4055. Return Value:
  4056. Returns a pointer to a terminal if found, or NULL otherwise.
  4057. --*/
  4058. {
  4059. PLIST_ENTRY CurrentEntry;
  4060. PTERMINAL FoundTerminal;
  4061. PTERMINAL Terminal;
  4062. ASSERT(KeIsQueuedLockHeld(IoTerminalListLock) != FALSE);
  4063. FoundTerminal = NULL;
  4064. CurrentEntry = IoTerminalList.Next;
  4065. while (CurrentEntry != &IoTerminalList) {
  4066. Terminal = LIST_VALUE(CurrentEntry, TERMINAL, ListEntry);
  4067. if (Terminal->SessionId == SessionId) {
  4068. FoundTerminal = Terminal;
  4069. }
  4070. CurrentEntry = CurrentEntry->Next;
  4071. }
  4072. return FoundTerminal;
  4073. }
  4074. VOID
  4075. IopRelinquishTerminal (
  4076. PTERMINAL Terminal
  4077. )
  4078. /*++
  4079. Routine Description:
  4080. This routine clears the controlling session and process group ID from
  4081. the given terminal, and signals everyone in the old process group.
  4082. Arguments:
  4083. Terminal - Supplies a pointer to the controlling terminal.
  4084. Return Value:
  4085. None.
  4086. --*/
  4087. {
  4088. PROCESS_GROUP_ID ProcessGroupId;
  4089. ProcessGroupId = Terminal->ProcessGroupId;
  4090. Terminal->SessionId = TERMINAL_INVALID_SESSION;
  4091. Terminal->ProcessGroupId = TERMINAL_INVALID_PROCESS_GROUP;
  4092. Terminal->SessionProcess = NULL;
  4093. ASSERT(ProcessGroupId != TERMINAL_INVALID_PROCESS_GROUP);
  4094. PsSignalProcessGroup(ProcessGroupId,
  4095. SIGNAL_CONTROLLING_TERMINAL_CLOSED);
  4096. PsSignalProcessGroup(ProcessGroupId, SIGNAL_CONTINUE);
  4097. return;
  4098. }