123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199 |
- /*++
- Copyright (c) 2013 Minoca Corp. All Rights Reserved
- Module Name:
- pty.c
- Abstract:
- This module implements terminal support.
- Author:
- Evan Green 10-May-2013
- Environment:
- Kernel
- --*/
- //
- // ------------------------------------------------------------------- Includes
- //
- #include <minoca/kernel/kernel.h>
- #include <minoca/lib/termlib.h>
- #include "iop.h"
- //
- // ---------------------------------------------------------------- Definitions
- //
- #define TERMINAL_ALLOCATION_TAG 0x216D7254 // '!mrT'
- #define TERMINAL_INITIAL_PERMISSIONS \
- (FILE_PERMISSION_USER_READ | FILE_PERMISSION_USER_WRITE | \
- FILE_PERMISSION_GROUP_READ | FILE_PERMISSION_GROUP_WRITE)
- #define TERMINAL_DIRECTORY_NAME "Terminal"
- #define TERMINAL_MASTER_NAME_FORMAT "Master%X"
- #define TERMINAL_SLAVE_NAME_FORMAT "Slave%X"
- #define TERMINAL_MAX_NAME_LENGTH 23
- #define TERMINAL_MAX_COMMAND_HISTORY 50
- #define TERMINAL_MAX_CANONICAL_OUTPUT 8
- //
- // Define the number of lines to scroll in canonical mode when page up/down
- // is seen.
- //
- #define TERMINAL_SCROLL_LINE_COUNT 5
- //
- // Define terminal limits. The input queue length must always be at least the
- // max canonical length since the line gets dumped into the input queue.
- //
- #define TERMINAL_INPUT_BUFFER_SIZE 512
- #define TERMINAL_CANONICAL_BUFFER_SIZE (TERMINAL_INPUT_BUFFER_SIZE - 1)
- #define TERMINAL_OUTPUT_BUFFER_SIZE 256
- //
- // Default control characters.
- //
- #define TERMINAL_DEFAULT_END_OF_FILE 0x04
- #define TERMINAL_DEFAULT_END_OF_LINE 0x00
- #define TERMINAL_DEFAULT_ERASE 0x7F
- #define TERMINAL_DEFAULT_INTERRUPT 0x03
- #define TERMINAL_DEFAULT_KILL 0x15
- #define TERMINAL_DEFAULT_QUIT 0x1C
- #define TERMINAL_DEFAULT_SUSPEND 0x1A
- #define TERMINAL_DEFAULT_START 0x11
- #define TERMINAL_DEFAULT_STOP 0x13
- //
- // Define the default baud rate terminals come up in.
- //
- #define TERMINAL_DEFAULT_BAUD_RATE 115200
- //
- // Define the default window size that terminals get initialized to.
- //
- #define TERMINAL_DEFAULT_ROWS 25
- #define TERMINAL_DEFAULT_COLUMNS 80
- //
- // Define terminal flags.
- //
- #define TERMINAL_FLAG_VIRGIN_LINE 0x00000001
- #define TERMINAL_FLAG_UNEDITED_LINE 0x0000002
- #define TERMINAL_FLAG_FAIL_OPENS 0x00000004
- //
- // Define the invalid session and process group IDs.
- //
- #define TERMINAL_INVALID_SESSION -1
- #define TERMINAL_INVALID_PROCESS_GROUP -1
- #define TERMINAL_POLL_ERRORS (POLL_EVENT_ERROR | POLL_EVENT_DISCONNECTED)
- //
- // --------------------------------------------------------------------- Macros
- //
- //
- // The terminal master is considered open if it has more than 1 reference. An
- // initial reference is taken upon creation, but that does not count towards
- // being opened.
- //
- #define IO_IS_TERMINAL_MASTER_OPEN(_Terminal) \
- ((_Terminal)->MasterReferenceCount > 1)
- //
- // ------------------------------------------------------ Data Type Definitions
- //
- typedef struct _TERMINAL_SLAVE TERMINAL_SLAVE, *PTERMINAL_SLAVE;
- /*++
- Structure Description:
- This structure defines terminal structure.
- Members:
- Header - Stores the standard object header.
- ListEntry - Stores pointers to the next and previous terminals in the
- global list.
- Number - Stores the terminal number.
- OutputBuffer - Stores the output buffer (buffer going out of the slave into
- the master).
- OutputBufferStart - Stores the first valid index of the output buffer.
- OutputBufferEnd - Stores the first invalid index of the output buffer. If
- this is equal to the start, then the buffer is empty.
- OutputLock - Stores a pointer to a lock serializing access to the output
- buffer.
- InputBuffer - Stores a pointer to the input buffer.
- InputBufferStart - Stores the first valid index of the input buffer.
- InputBufferEnd - Stores the first invalid index of the input buffer. If
- this is equal to the start, then the buffer is empty.
- WorkingInputBuffer - Stores the current (unfinished) line in canonical
- mode.
- WorkingInputCursor - Stores the current position of the cursor in the
- working input buffer.
- WorkingInputLength - Stores the valid length of the working input buffer.
- InputLock - Stores a pointer to a lock serializing access to the
- working input buffer.
- Settings - Stores the current terminal settings.
- Flags - Stores a bitfield of flags. See TERMINAL_FLAG_* definitions. This
- field is protected by the terminal output lock.
- KeyData - Stores the data for the key currently being parsed. This is only
- used in canonical mode.
- SlaveHandles - Stores the count of open slave side handles, not counting
- those opened with no access.
- ProcessGroupId - Stores the owning process group ID of the terminal.
- SessionId - Stores the owning session ID of the terminal.
- ConnectionLock - Stores a spin lock that synchronizes the connection
- between the master and the slave, ensuring they both shut down in an
- orderly fashion.
- MasterReferenceCount - Stores the number of references on the master. This
- amounts to the number of open file handles plus one additional
- reference set on initialization.
- Slave - Stores a pointer to the corresponding slave object.
- SlaveFileObject - Stores a pointer to the slave's file object.
- MasterFileObject - Stores a pointer to the master's file object.
- WindowSize - Stores the window size of the terminal.
- ModemStatus - Stores the modem status bits.
- HardwareHandle - Stores an optional handle to the hardware device backing
- this terminal.
- SlavePathPoint - Stores the path point of the slave device, used to unlink
- it when the last master handle is closed.
- --*/
- typedef struct _TERMINAL {
- OBJECT_HEADER Header;
- LIST_ENTRY ListEntry;
- ULONG Number;
- PSTR OutputBuffer;
- ULONG OutputBufferStart;
- ULONG OutputBufferEnd;
- PQUEUED_LOCK OutputLock;
- PSTR InputBuffer;
- ULONG InputBufferStart;
- ULONG InputBufferEnd;
- PSTR WorkingInputBuffer;
- ULONG WorkingInputCursor;
- ULONG WorkingInputLength;
- PQUEUED_LOCK InputLock;
- TERMINAL_SETTINGS Settings;
- TERMINAL_KEY_DATA KeyData;
- ULONG Flags;
- UINTN SlaveHandles;
- PROCESS_GROUP_ID ProcessGroupId;
- SESSION_ID SessionId;
- ULONG MasterReferenceCount;
- PTERMINAL_SLAVE Slave;
- PFILE_OBJECT SlaveFileObject;
- PFILE_OBJECT MasterFileObject;
- TERMINAL_WINDOW_SIZE WindowSize;
- INT ModemStatus;
- PIO_HANDLE HardwareHandle;
- PATH_POINT SlavePathPoint;
- } TERMINAL, *PTERMINAL;
- /*++
- Structure Description:
- This structure defines the slave terminal structure.
- Members:
- Header - Stores the standard object header.
- Master - Stores a pointer to the master terminal.
- --*/
- struct _TERMINAL_SLAVE {
- OBJECT_HEADER Header;
- PTERMINAL Master;
- };
- /*++
- Structure Description:
- This structure defines the parameters sent during a creation request of
- a terminal object.
- Members:
- SlaveCreatePermissions - Store a pointer to the permissions used when
- creating the slave side.
- Master - Stores a pointer to the master terminal. When creating a master
- terminal, this parameter will be filled in during create. When creating
- a slave, this parameter must already be filled in and will be used.
- --*/
- typedef struct _TERMINAL_CREATION_PARAMETERS {
- FILE_PERMISSIONS SlaveCreatePermissions;
- PTERMINAL Master;
- } TERMINAL_CREATION_PARAMETERS, *PTERMINAL_CREATION_PARAMETERS;
- //
- // ----------------------------------------------- Internal Function Prototypes
- //
- KSTATUS
- IopCreateTerminalObject (
- FILE_PERMISSIONS CreatePermissions,
- PTERMINAL *NewTerminal
- );
- VOID
- IopDestroyTerminal (
- PVOID TerminalObject
- );
- KSTATUS
- IopTerminalMasterWrite (
- PFILE_OBJECT FileObject,
- PIO_CONTEXT IoContext
- );
- KSTATUS
- IopTerminalSlaveWrite (
- PFILE_OBJECT FileObject,
- PIO_CONTEXT IoContext
- );
- KSTATUS
- IopTerminalMasterRead (
- PFILE_OBJECT FileObject,
- PIO_CONTEXT IoContext
- );
- KSTATUS
- IopTerminalSlaveRead (
- PFILE_OBJECT FileObject,
- PIO_CONTEXT IoContext
- );
- KSTATUS
- IopTerminalWriteOutputBuffer (
- PTERMINAL Terminal,
- PVOID Buffer,
- UINTN SizeInBytes,
- ULONG RepeatCount,
- ULONG TimeoutInMilliseconds
- );
- ULONG
- IopTerminalGetInputBufferSpace (
- PTERMINAL Terminal
- );
- ULONG
- IopTerminalGetOutputBufferSpace (
- PTERMINAL Terminal
- );
- KSTATUS
- IopTerminalFixUpCanonicalLine (
- PTERMINAL Terminal,
- ULONG TimeoutInMilliseconds,
- ULONG DirtyRegionBegin,
- ULONG DirtyRegionEnd,
- ULONG CurrentScreenPosition
- );
- BOOL
- IopTerminalProcessEditingCharacter (
- PTERMINAL Terminal,
- CHAR Character,
- ULONG TimeoutInMilliseconds,
- PULONG DirtyRegionBegin,
- PULONG DirtyRegionEnd,
- PULONG ScreenCursorPosition,
- PBOOL OutputWritten
- );
- KSTATUS
- IopTerminalUserBufferCopy (
- BOOL FromKernelMode,
- BOOL FromBuffer,
- PVOID UserBuffer,
- PVOID LocalBuffer,
- UINTN Size
- );
- KSTATUS
- IopTerminalFlushOutputToDevice (
- PTERMINAL Terminal
- );
- VOID
- IopTerminalDisassociate (
- PTERMINAL Terminal
- );
- BOOL
- IopTerminalDisassociateIterator (
- PVOID Context,
- PKPROCESS Process
- );
- //
- // -------------------------------------------------------------------- Globals
- //
- //
- // Store a pointer to the global terminal directory.
- //
- PVOID IoTerminalDirectory;
- //
- // Store the global list of terminals.
- //
- LIST_ENTRY IoTerminalList;
- PQUEUED_LOCK IoTerminalListLock;
- //
- // Store a pointer to the local console terminal.
- //
- PIO_HANDLE IoLocalConsole;
- //
- // ------------------------------------------------------------------ Functions
- //
- KERNEL_API
- KSTATUS
- IoCreateTerminal (
- BOOL FromKernelMode,
- PIO_HANDLE MasterDirectory,
- PIO_HANDLE SlaveDirectory,
- PSTR MasterPath,
- UINTN MasterPathLength,
- PSTR SlavePath,
- UINTN SlavePathLength,
- ULONG MasterAccess,
- ULONG MasterOpenFlags,
- FILE_PERMISSIONS MasterCreatePermissions,
- FILE_PERMISSIONS SlaveCreatePermissions,
- PIO_HANDLE *MasterHandle
- )
- /*++
- Routine Description:
- This routine creates and opens a new terminal master.
- Arguments:
- FromKernelMode - Supplies a boolean indicating whether this request is
- originating from kernel mode (and should use the root path as a base)
- or user mode.
- MasterDirectory - Supplies an optional pointer to an open handle to a
- directory for relative paths when creating the master. Supply NULL to
- use the current working directory.
- SlaveDirectory - Supplies an optional pointer to an open handle to a
- directory for relative paths when creating the slave. Supply NULL to
- use the current working directory.
- MasterPath - Supplies an optional pointer to the path to create for the
- master.
- MasterPathLength - Supplies the length of the master side path buffer in
- bytes, including the null terminator.
- SlavePath - Supplies an optional pointer to the path to create for the
- master.
- SlavePathLength - Supplies the length of the slave side path buffer in
- bytes, including the null terminator.
- MasterAccess - Supplies the desired access permissions to the master side
- handle. See IO_ACCESS_* definitions.
- MasterOpenFlags - Supplies the open flags to use when opening the master.
- MasterCreatePermissions - Supplies the permissions to apply to the created
- master side.
- SlaveCreatePermissions - Supplies the permission to apply to the created
- slave side.
- MasterHandle - Supplies a pointer where a handle to the master side of the
- new terminal will be returned.
- Return Value:
- Status code.
- --*/
- {
- TERMINAL_CREATION_PARAMETERS CreationParameters;
- PIO_HANDLE SlaveHandle;
- KSTATUS Status;
- RtlZeroMemory(&CreationParameters, sizeof(TERMINAL_CREATION_PARAMETERS));
- CreationParameters.SlaveCreatePermissions = SlaveCreatePermissions;
- //
- // First try to open the master.
- //
- MasterOpenFlags |= OPEN_FLAG_CREATE | OPEN_FLAG_FAIL_IF_EXISTS;
- Status = IopOpen(FromKernelMode,
- MasterDirectory,
- MasterPath,
- MasterPathLength,
- MasterAccess,
- MasterOpenFlags,
- IoObjectTerminalMaster,
- &CreationParameters,
- MasterCreatePermissions,
- MasterHandle);
- if (!KSUCCESS(Status)) {
- return Status;
- }
- //
- // The master put itself in the creation parameters, which are now passed
- // down when trying to create the slave (which is mostly just a matter of
- // creating the path entry now).
- //
- MasterOpenFlags |= OPEN_FLAG_NO_CONTROLLING_TERMINAL;
- Status = IopOpen(FromKernelMode,
- SlaveDirectory,
- SlavePath,
- SlavePathLength,
- 0,
- MasterOpenFlags,
- IoObjectTerminalSlave,
- &CreationParameters,
- SlaveCreatePermissions,
- &SlaveHandle);
- if (!KSUCCESS(Status)) {
- IoClose(*MasterHandle);
- }
- //
- // Copy the path entry, then close the slave handle.
- //
- ASSERT((CreationParameters.Master != NULL) &&
- (CreationParameters.Master->SlavePathPoint.PathEntry == NULL));
- IO_COPY_PATH_POINT(&(CreationParameters.Master->SlavePathPoint),
- &(SlaveHandle->PathPoint));
- IO_PATH_POINT_ADD_REFERENCE(&(CreationParameters.Master->SlavePathPoint));
- IoClose(SlaveHandle);
- return Status;
- }
- KERNEL_API
- KSTATUS
- IoOpenLocalTerminalMaster (
- PIO_HANDLE *TerminalMaster
- )
- /*++
- Routine Description:
- This routine opens the master side of the local console terminal. This
- routine is intended to be used by the input and output devices that
- actually service the local console (the user input driver and video
- console driver).
- Arguments:
- TerminalMaster - Supplies a pointer where the I/O handle representing the
- master side of the local console terminal will be returned.
- Return Value:
- Status code.
- --*/
- {
- if (IoLocalConsole == NULL) {
- return STATUS_NOT_READY;
- }
- IoIoHandleAddReference(IoLocalConsole);
- *TerminalMaster = IoLocalConsole;
- return STATUS_SUCCESS;
- }
- KERNEL_API
- KSTATUS
- IoOpenControllingTerminal (
- PIO_HANDLE *IoHandle
- )
- /*++
- Routine Description:
- This routine attempts to open the current process' controlling terminal.
- Arguments:
- IoHandle - Supplies a pointer where the open I/O handle will be returned on
- success.
- Return Value:
- Status code.
- --*/
- {
- PIO_HANDLE Handle;
- ULONG PreviousValue;
- PKPROCESS Process;
- KSTATUS Status;
- *IoHandle = NULL;
- Process = PsGetCurrentProcess();
- KeAcquireQueuedLock(IoTerminalListLock);
- if (Process->ControllingTerminal == NULL) {
- Status = STATUS_NO_SUCH_DEVICE;
- } else {
- //
- // This part is a little fishy. Manually attempt to increment the
- // reference count on the handle. If the handle reference count was
- // zero, quietly back out. Because closing the slave acquires the
- // terminal list lock held here, the handle cannot disappear entirely.
- // But it can be in the process of closing. This concept was not
- // generalized into the I/O handle code because the idea of not
- // having a reference on the handle but somehow ensuring that it
- // couldn't disappear did not seem like a legit contract to create a
- // generic I/O handle function around.
- //
- Handle = Process->ControllingTerminal;
- PreviousValue = RtlAtomicAdd32(&(Handle->ReferenceCount), 1);
- if (PreviousValue == 0) {
- RtlAtomicAdd32(&(Handle->ReferenceCount), -1);
- Status = STATUS_NO_SUCH_DEVICE;
- } else {
- *IoHandle = Handle;
- Status = STATUS_SUCCESS;
- }
- }
- KeReleaseQueuedLock(IoTerminalListLock);
- return Status;
- }
- KERNEL_API
- KSTATUS
- IoSetTerminalSettings (
- PIO_HANDLE TerminalHandle,
- PTERMINAL_SETTINGS NewSettings,
- PTERMINAL_SETTINGS OriginalSettings,
- TERMINAL_CHANGE_BEHAVIOR When
- )
- /*++
- Routine Description:
- This routine gets or sets the current terminal settings.
- Arguments:
- TerminalHandle - Supplies a pointer to the I/O handle of the terminal to
- change.
- NewSettings - Supplies an optional pointer to the new terminal settings.
- If this pointer is NULL, then the current settings will be retrieved
- but no changes will be made.
- OriginalSettings - Supplies an optional pointer where the current
- settings will be returned.
- When - Supplies when the new change should occur.
- Return Value:
- Status code.
- --*/
- {
- PFILE_OBJECT FileObject;
- KSTATUS Status;
- PTERMINAL Terminal;
- PTERMINAL_SLAVE TerminalSlave;
- //
- // Get a pointer to the actual terminal structure.
- //
- FileObject = TerminalHandle->FileObject;
- if (FileObject->Properties.Type == IoObjectTerminalMaster) {
- Terminal = FileObject->SpecialIo;
- } else if (FileObject->Properties.Type == IoObjectTerminalSlave) {
- TerminalSlave = FileObject->SpecialIo;
- Terminal = TerminalSlave->Master;
- } else {
- return STATUS_NOT_A_TERMINAL;
- }
- ASSERT(KeGetRunLevel() == RunLevelLow);
- //
- // Lock down the terminal for this. The order of lock acquisition is
- // important.
- //
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- if (OriginalSettings != NULL) {
- RtlCopyMemory(OriginalSettings,
- &(Terminal->Settings),
- sizeof(TERMINAL_SETTINGS));
- }
- if (NewSettings != NULL) {
- //
- // Fail if an unsupported feature was requested. Consider adding
- // support for said feature.
- //
- if (((NewSettings->InputFlags &
- TERMINAL_UNIMPLEMENTED_INPUT_FLAGS) != 0) ||
- ((NewSettings->OutputFlags &
- TERMINAL_UNIMPLEMENTED_OUTPUT_FLAGS) != 0) ||
- ((NewSettings->ControlFlags &
- TERMINAL_UNIMPLEMENTED_CONTROL_FLAGS) != 0)) {
- ASSERT(FALSE);
- Status = STATUS_NOT_SUPPORTED;
- goto SetTerminalSettingsEnd;
- }
- RtlCopyMemory(&(Terminal->Settings),
- NewSettings,
- sizeof(TERMINAL_SETTINGS));
- }
- //
- // If the user asked, remove all input.
- //
- if (When == TerminalChangeAfterOutputFlushInput) {
- Terminal->InputBufferStart = 0;
- Terminal->InputBufferEnd = 0;
- }
- Status = STATUS_SUCCESS;
- SetTerminalSettingsEnd:
- KeReleaseQueuedLock(Terminal->OutputLock);
- KeReleaseQueuedLock(Terminal->InputLock);
- return Status;
- }
- KERNEL_API
- KSTATUS
- IoTerminalSetDevice (
- PIO_HANDLE TerminalMaster,
- PIO_HANDLE DeviceHandle
- )
- /*++
- Routine Description:
- This routine associates or disassociates a terminal object with a device.
- Writes to the terminal slave will be forwarded to the associated terminal,
- as will changes to the terminal settings. If a device is being associated
- with the terminal, then the new settings will be sent to the device
- immediately in this routine.
- Arguments:
- TerminalMaster - Supplies a handle to the terminal master.
- DeviceHandle - Supplies a handle to the terminal hardware device. Any
- previously associated handle will be closed. Supply NULL to
- disassociate the terminal with a device. Upon success, this routine
- takes ownership of this device handle, and the caller should not close
- it manually.
- Return Value:
- Status code.
- --*/
- {
- PFILE_OBJECT FileObject;
- KSTATUS Status;
- PTERMINAL Terminal;
- FileObject = TerminalMaster->FileObject;
- if (FileObject->Properties.Type != IoObjectTerminalMaster) {
- Status = STATUS_NOT_A_TERMINAL;
- goto TerminalSetDeviceEnd;
- }
- Terminal = FileObject->SpecialIo;
- Status = STATUS_SUCCESS;
- //
- // Remove the old handle.
- //
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- if (Terminal->HardwareHandle != NULL) {
- IoClose(Terminal->HardwareHandle);
- }
- Terminal->HardwareHandle = DeviceHandle;
- //
- // If a new device is being associated with the terminal, send the settings
- // down to it now.
- //
- if (DeviceHandle != NULL) {
- Status = IoUserControl(DeviceHandle,
- TerminalControlSetAttributes,
- TRUE,
- &(Terminal->Settings),
- sizeof(TERMINAL_SETTINGS));
- }
- KeReleaseQueuedLock(Terminal->OutputLock);
- KeReleaseQueuedLock(Terminal->InputLock);
- TerminalSetDeviceEnd:
- return Status;
- }
- KSTATUS
- IopInitializeTerminalSupport (
- VOID
- )
- /*++
- Routine Description:
- This routine is called during system initialization to set up support for
- terminals.
- Arguments:
- None.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS Status;
- INITIALIZE_LIST_HEAD(&IoTerminalList);
- IoTerminalListLock = KeCreateQueuedLock();
- if (IoTerminalListLock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeTerminalSupportEnd;
- }
- //
- // Create the Terminal object directory.
- //
- IoTerminalDirectory = ObCreateObject(ObjectDirectory,
- NULL,
- TERMINAL_DIRECTORY_NAME,
- sizeof(TERMINAL_DIRECTORY_NAME),
- sizeof(OBJECT_HEADER),
- NULL,
- OBJECT_FLAG_USE_NAME_DIRECTLY,
- TERMINAL_ALLOCATION_TAG);
- if (IoTerminalDirectory == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto InitializeTerminalSupportEnd;
- }
- //
- // Create a local console terminal.
- //
- Status = IoCreateTerminal(TRUE,
- NULL,
- NULL,
- NULL,
- 0,
- NULL,
- 0,
- IO_ACCESS_READ | IO_ACCESS_WRITE,
- 0,
- TERMINAL_DEFAULT_PERMISSIONS,
- TERMINAL_DEFAULT_PERMISSIONS,
- &IoLocalConsole);
- if (!KSUCCESS(Status)) {
- goto InitializeTerminalSupportEnd;
- }
- Status = STATUS_SUCCESS;
- InitializeTerminalSupportEnd:
- return Status;
- }
- KSTATUS
- IopTerminalOpenMaster (
- PIO_HANDLE IoHandle
- )
- /*++
- Routine Description:
- This routine is called when a master terminal was just opened.
- Arguments:
- IoHandle - Supplies a pointer to the I/O handle for the terminal.
- Return Value:
- Status code.
- --*/
- {
- PFILE_OBJECT FileObject;
- KSTATUS Status;
- PTERMINAL Terminal;
- FileObject = IoHandle->FileObject;
- ASSERT(FileObject->Properties.Type == IoObjectTerminalMaster);
- //
- // If no access is requested, then the special I/O terminal object does not
- // need to be present, and in fact, may not be.
- //
- if (IoHandle->Access == 0) {
- return STATUS_SUCCESS;
- }
- Terminal = FileObject->SpecialIo;
- if (Terminal == NULL) {
- Status = STATUS_NOT_READY;
- return Status;
- }
- ASSERT(Terminal->Header.Type == ObjectTerminalMaster);
- if (((Terminal->Flags & TERMINAL_FLAG_FAIL_OPENS) != 0) &&
- (!KSUCCESS(PsCheckPermission(PERMISSION_SYSTEM_ADMINISTRATOR)))) {
- return STATUS_RESOURCE_IN_USE;
- }
- //
- // Increment the number of parties that have the master terminal open. If
- // the initial reference taken on creation is gone, then this master
- // terminal is on its way out. That is, do not resurrect the master from 0
- // references.
- //
- Status = STATUS_SUCCESS;
- KeAcquireQueuedLock(IoTerminalListLock);
- if (Terminal->MasterReferenceCount == 0) {
- Status = STATUS_NO_SUCH_FILE;
- } else {
- Terminal->MasterReferenceCount += 1;
- }
- KeReleaseQueuedLock(IoTerminalListLock);
- return Status;
- }
- KSTATUS
- IopTerminalCloseMaster (
- PIO_HANDLE IoHandle
- )
- /*++
- Routine Description:
- This routine is called when a master terminal was just closed.
- Arguments:
- IoHandle - Supplies a pointer to the handle to close.
- Return Value:
- Status code.
- --*/
- {
- ULONG Events;
- PFILE_OBJECT FileObject;
- PTERMINAL Terminal;
- FileObject = IoHandle->FileObject;
- ASSERT(FileObject->Properties.Type == IoObjectTerminalMaster);
- //
- // Handles with no access never really counted and the special I/O object
- // may not have ever been present.
- //
- if (IoHandle->Access == 0) {
- return STATUS_SUCCESS;
- }
- Terminal = FileObject->SpecialIo;
- ASSERT(Terminal->Header.Type == ObjectTerminalMaster);
- //
- // Just decrement the reference count.
- //
- KeAcquireQueuedLock(IoTerminalListLock);
- ASSERT((Terminal->MasterReferenceCount > 1) &&
- (Terminal->MasterReferenceCount < 0x10000000));
- Terminal->MasterReferenceCount -= 1;
- //
- // If this was the last reference to match an open of the master, close the
- // connection between the master and the slave.
- //
- if (Terminal->MasterReferenceCount == 1) {
- //
- // Decrement the original reference, preventing any additional opens of
- // the master terminal during the destruction process. It is possible
- // that a path walk has already taken another reference on the master's
- // path entry.
- //
- Terminal->MasterReferenceCount -= 1;
- if (Terminal->SlaveFileObject != NULL) {
- Events = POLL_EVENT_IN | POLL_EVENT_OUT | POLL_EVENT_ERROR |
- POLL_EVENT_DISCONNECTED;
- IoSetIoObjectState(Terminal->SlaveFileObject->IoState,
- Events,
- TRUE);
- }
- //
- // Unlink the master.
- //
- IopDeleteByHandle(TRUE, IoHandle, 0);
- //
- // Unlink the slave.
- //
- if (Terminal->SlavePathPoint.PathEntry != NULL) {
- IopDeletePathPoint(TRUE, &(Terminal->SlavePathPoint), 0);
- IO_PATH_POINT_RELEASE_REFERENCE(&(Terminal->SlavePathPoint));
- Terminal->SlavePathPoint.PathEntry = NULL;
- }
- //
- // Release the initial reference on the slave file object taken when
- // the master was created.
- //
- if (Terminal->SlaveFileObject != NULL) {
- IopFileObjectReleaseReference(Terminal->SlaveFileObject);
- }
- }
- KeReleaseQueuedLock(IoTerminalListLock);
- return STATUS_SUCCESS;
- }
- KSTATUS
- IopTerminalOpenSlave (
- PIO_HANDLE IoHandle
- )
- /*++
- Routine Description:
- This routine opens the slave side of a terminal object.
- Arguments:
- IoHandle - Supplies a pointer to the I/O handle for the terminal.
- Return Value:
- Status code.
- --*/
- {
- PFILE_OBJECT FileObject;
- PIO_OBJECT_STATE MasterIoState;
- PKPROCESS Process;
- PTERMINAL_SLAVE Slave;
- KSTATUS Status;
- PTERMINAL Terminal;
- BOOL TerminalLocksHeld;
- FileObject = IoHandle->FileObject;
- ASSERT(FileObject->Properties.Type == IoObjectTerminalSlave);
- //
- // If the caller doesn't actually want any access, then just let it slide.
- // The special I/O object may not be initialized.
- //
- if (IoHandle->Access == 0) {
- return STATUS_SUCCESS;
- }
- Slave = FileObject->SpecialIo;
- if (Slave == NULL) {
- return STATUS_NOT_READY;
- }
- ASSERT(Slave->Header.Type == ObjectTerminalSlave);
- TerminalLocksHeld = FALSE;
- KeAcquireQueuedLock(IoTerminalListLock);
- //
- // Get the master terminal. Synchronize this to avoid situations where the
- // master gets cleaned up after this pointer is read. Also synchronize with
- // the setting of the owning process group and session. Some of the user
- // controls synchronize terminal lookups with session ownership changes.
- //
- Terminal = Slave->Master;
- if (((Terminal->Flags & TERMINAL_FLAG_FAIL_OPENS) != 0) &&
- (!KSUCCESS(PsCheckPermission(PERMISSION_SYSTEM_ADMINISTRATOR)))) {
- Status = STATUS_RESOURCE_IN_USE;
- goto TerminalOpenSlaveEnd;
- }
- if (IO_IS_TERMINAL_MASTER_OPEN(Terminal) != FALSE) {
- ObAddReference(Terminal);
- IopFileObjectAddReference(Terminal->MasterFileObject);
- } else {
- Terminal = NULL;
- }
- if (Terminal == NULL) {
- Status = STATUS_TOO_LATE;
- goto TerminalOpenSlaveEnd;
- }
- //
- // Synchronize the check and set of the owning process group and session
- // with other opens and requests to change the process group and session.
- //
- TerminalLocksHeld = TRUE;
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- //
- // If the terminal is already open in another session, refuse to open.
- //
- Process = PsGetCurrentProcess();
- Terminal->SlaveHandles += 1;
- //
- // Clear the error that may have been set when the last previous slave
- // was closed.
- //
- if (Terminal->SlaveHandles == 1) {
- MasterIoState = Terminal->MasterFileObject->IoState;
- IoSetIoObjectState(MasterIoState, POLL_EVENT_DISCONNECTED, FALSE);
- //
- // Also clear the master in event if there's nothing to actually read.
- //
- if (IopTerminalGetOutputBufferSpace(Terminal) ==
- TERMINAL_OUTPUT_BUFFER_SIZE - 1) {
- IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, FALSE);
- }
- }
- //
- // Make this terminal the controlling terminal for the process if
- // 1) The no controlling terminal flag is not set.
- // 2) The terminal is not already assigned to another session.
- // 3) This process is a session leader.
- // 4) This process does not already have a controlling terminal.
- //
- if (((IoHandle->OpenFlags & OPEN_FLAG_NO_CONTROLLING_TERMINAL) == 0) &&
- (Terminal->SessionId == TERMINAL_INVALID_SESSION) &&
- (PsIsSessionLeader(Process) != FALSE) &&
- (Process->ControllingTerminal == NULL)) {
- Process->ControllingTerminal = IoHandle;
- Terminal->ProcessGroupId = Process->Identifiers.ProcessGroupId;
- Terminal->SessionId = Process->Identifiers.SessionId;
- }
- Status = STATUS_SUCCESS;
- TerminalOpenSlaveEnd:
- if (TerminalLocksHeld != FALSE) {
- KeReleaseQueuedLock(Terminal->InputLock);
- KeReleaseQueuedLock(Terminal->OutputLock);
- }
- KeReleaseQueuedLock(IoTerminalListLock);
- return Status;
- }
- KSTATUS
- IopTerminalCloseSlave (
- PIO_HANDLE IoHandle
- )
- /*++
- Routine Description:
- This routine is called when a master terminal was just closed.
- Arguments:
- IoHandle - Supplies a pointer to the handle to close.
- Return Value:
- Status code.
- --*/
- {
- PFILE_OBJECT FileObject;
- PKPROCESS Process;
- PTERMINAL_SLAVE Slave;
- PTERMINAL Terminal;
- FileObject = IoHandle->FileObject;
- ASSERT(FileObject->Properties.Type == IoObjectTerminalSlave);
- //
- // Handles with no access never really counted and the special I/O object
- // may not have been initialized.
- //
- if (IoHandle->Access == 0) {
- return STATUS_SUCCESS;
- }
- Slave = FileObject->SpecialIo;
- ASSERT(Slave->Header.Type == ObjectTerminalSlave);
- Process = PsGetCurrentProcess();
- Terminal = Slave->Master;
- if (PsIsSessionLeader(Process) != FALSE) {
- KeAcquireQueuedLock(IoTerminalListLock);
- }
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- ASSERT(Terminal->SlaveHandles != 0);
- Terminal->SlaveHandles -= 1;
- //
- // Tell the master no one's listening.
- //
- if (Terminal->SlaveHandles == 0) {
- IoSetIoObjectState(Terminal->MasterFileObject->IoState,
- POLL_EVENT_IN | POLL_EVENT_DISCONNECTED,
- TRUE);
- }
- //
- // If this is a session leader closing its controlling terminal, then
- // disassociate the controlling terminal for the entire session.
- //
- if ((PsIsSessionLeader(Process) != FALSE) &&
- (IoHandle == Process->ControllingTerminal)) {
- //
- // If the session leader is dying, send a hangup signal to the
- // foreground process group.
- //
- if (Process->ThreadCount == 0) {
- PsSignalProcessGroup(Terminal->ProcessGroupId,
- SIGNAL_CONTROLLING_TERMINAL_CLOSED);
- }
- IopTerminalDisassociate(Terminal);
- ASSERT(Process->ControllingTerminal == NULL);
- }
- KeReleaseQueuedLock(Terminal->OutputLock);
- KeReleaseQueuedLock(Terminal->InputLock);
- if (PsIsSessionLeader(Process) != FALSE) {
- KeReleaseQueuedLock(IoTerminalListLock);
- }
- //
- // Release the reference on the master taken during opening, which may
- // allow the master to free itself.
- //
- ObReleaseReference(Slave->Master);
- IopFileObjectReleaseReference(Slave->Master->MasterFileObject);
- return STATUS_SUCCESS;
- }
- KSTATUS
- IopPerformTerminalMasterIoOperation (
- PIO_HANDLE Handle,
- PIO_CONTEXT IoContext
- )
- /*++
- Routine Description:
- This routine reads from or writes to the master end of a terminal.
- Arguments:
- Handle - Supplies a pointer to the master terminal I/O handle.
- IoContext - Supplies a pointer to the I/O context.
- Return Value:
- Status code. A failing status code does not necessarily mean no I/O made it
- in or out. Check the bytes completed value in the I/O context to find out
- how much occurred.
- --*/
- {
- PFILE_OBJECT FileObject;
- KSTATUS Status;
- FileObject = Handle->FileObject;
- ASSERT(IoContext->IoBuffer != NULL);
- ASSERT(FileObject->Properties.Type == IoObjectTerminalMaster);
- if (IoContext->Write != FALSE) {
- Status = IopTerminalMasterWrite(FileObject, IoContext);
- } else {
- Status = IopTerminalMasterRead(FileObject, IoContext);
- }
- return Status;
- }
- KSTATUS
- IopPerformTerminalSlaveIoOperation (
- PIO_HANDLE Handle,
- PIO_CONTEXT IoContext
- )
- /*++
- Routine Description:
- This routine reads from or writes to the slave end of a terminal.
- Arguments:
- Handle - Supplies a pointer to the slave terminal I/O handle.
- IoContext - Supplies a pointer to the I/O context.
- Return Value:
- Status code. A failing status code does not necessarily mean no I/O made it
- in or out. Check the bytes completed value in the I/O context to find out
- how much occurred.
- --*/
- {
- PFILE_OBJECT FileObject;
- KSTATUS Status;
- FileObject = Handle->FileObject;
- ASSERT(IoContext->IoBuffer != NULL);
- ASSERT(FileObject->Properties.Type == IoObjectTerminalSlave);
- if (IoContext->Write != FALSE) {
- Status = IopTerminalSlaveWrite(FileObject, IoContext);
- } else {
- Status = IopTerminalSlaveRead(FileObject, IoContext);
- }
- return Status;
- }
- KSTATUS
- IopCreateTerminal (
- IO_OBJECT_TYPE Type,
- PVOID OverrideParameter,
- FILE_PERMISSIONS CreatePermissions,
- PFILE_OBJECT *FileObject
- )
- /*++
- Routine Description:
- This routine creates a terminal master or slave.
- Arguments:
- Type - Supplies the type of special object to create.
- OverrideParameter - Supplies an optional parameter to send along with the
- override type.
- CreatePermissions - Supplies the permissions to assign to the new file.
- FileObject - Supplies a pointer where a pointer to the new file object
- will be returned on success.
- Return Value:
- Status code.
- --*/
- {
- BOOL Created;
- PTERMINAL_CREATION_PARAMETERS CreationParameters;
- PLIST_ENTRY CurrentEntry;
- BOOL ListLockHeld;
- CHAR Name[TERMINAL_MAX_NAME_LENGTH];
- ULONG NameLength;
- PFILE_OBJECT NewFileObject;
- ULONG Number;
- FILE_PROPERTIES Properties;
- KSTATUS Status;
- PTERMINAL Terminal;
- PTERMINAL TerminalAfter;
- PLIST_ENTRY TerminalAfterEntry;
- CreationParameters = OverrideParameter;
- ListLockHeld = FALSE;
- //
- // If the object came up from out of the file system, don't actually
- // create anything. The common case here is querying file properties.
- //
- if (CreationParameters == NULL) {
- ASSERT(*FileObject != NULL);
- Status = STATUS_SUCCESS;
- goto CreateTerminalEnd;
- }
- NewFileObject = NULL;
- //
- // Create the slave file object.
- //
- if (Type == IoObjectTerminalSlave) {
- ASSERT(CreationParameters->Master != NULL);
- Terminal = CreationParameters->Master;
- ASSERT(Terminal->SlaveFileObject == NULL);
- //
- // Create a new file object if there isn't one already.
- //
- if (*FileObject == NULL) {
- IopFillOutFilePropertiesForObject(&Properties,
- &(Terminal->Slave->Header));
- Properties.Type = IoObjectTerminalSlave;
- Properties.Permissions = CreatePermissions;
- Status = IopCreateOrLookupFileObject(&Properties,
- ObGetRootObject(),
- 0,
- &NewFileObject,
- &Created);
- if (!KSUCCESS(Status)) {
- //
- // Release the reference from when the properties were filled
- // out above.
- //
- ObReleaseReference(Terminal->Slave);
- goto CreateTerminalEnd;
- }
- ASSERT(Created != FALSE);
- *FileObject = NewFileObject;
- //
- // With the file object created, but not yet ready, go ahead and
- // name the terminal slave object. Once it has a name it can be
- // found by other threads via path lookup, but those threads will
- // have to wait on the file object's ready event before proceeding.
- //
- ASSERT(Terminal->Number != MAX_ULONG);
- //
- // Create the terminal name string (on the stack, it gets copied by
- // the object manager) and then set the name in the object.
- //
- NameLength = RtlPrintToString(Name,
- TERMINAL_MAX_NAME_LENGTH,
- CharacterEncodingDefault,
- TERMINAL_SLAVE_NAME_FORMAT,
- Terminal->Number);
- Status = ObNameObject(Terminal->Slave,
- Name,
- NameLength,
- TERMINAL_ALLOCATION_TAG,
- FALSE);
- if (!KSUCCESS(Status)) {
- ASSERT(Status != STATUS_TOO_LATE);
- goto CreateTerminalEnd;
- }
- }
- //
- // Add a reference since the master holds a reference to the slave
- // file object.
- //
- IopFileObjectAddReference(*FileObject);
- //
- // By setting the slave file object to non-null, this code is
- // transferring the reference originally held by the master when the
- // slave was created over to the file object special I/O field.
- //
- Terminal->SlaveFileObject = *FileObject;
- ASSERT((*FileObject)->SpecialIo == NULL);
- (*FileObject)->SpecialIo = Terminal->Slave;
- //
- // Create a master, which creates the slave object as well.
- //
- } else {
- ASSERT(Type == IoObjectTerminalMaster);
- ASSERT(CreationParameters->Master == NULL);
- Terminal = NULL;
- //
- // Create the terminal object. This reference will get transferred to
- // the file object special I/O field on success.
- //
- Status = IopCreateTerminalObject(
- CreationParameters->SlaveCreatePermissions,
- &Terminal);
- if (!KSUCCESS(Status)) {
- goto CreateTerminalEnd;
- }
- //
- // Create a file object if necessary. This adds a reference on the
- // object.
- //
- if (*FileObject == NULL) {
- IopFillOutFilePropertiesForObject(&Properties, &(Terminal->Header));
- Properties.Type = IoObjectTerminalMaster;
- Properties.Permissions = CreatePermissions;
- Status = IopCreateOrLookupFileObject(&Properties,
- ObGetRootObject(),
- 0,
- &NewFileObject,
- &Created);
- if (!KSUCCESS(Status)) {
- //
- // Release both the references taken by creating the object and
- // filling out the file properties.
- //
- ObReleaseReference(Terminal);
- ObReleaseReference(Terminal);
- goto CreateTerminalEnd;
- }
- ASSERT(Created != FALSE);
- *FileObject = NewFileObject;
- //
- // With the file object created, but not yet ready, go ahead and
- // name the terminal master object. Once it has a name it can be
- // found by other threads via path lookup, but those threads will
- // have to wait on the file object's ready event before proceeding.
- //
- Number = 0;
- KeAcquireQueuedLock(IoTerminalListLock);
- ListLockHeld = TRUE;
- LIST_REMOVE(&(Terminal->ListEntry));
- CurrentEntry = IoTerminalList.Next;
- TerminalAfterEntry = &IoTerminalList;
- while (CurrentEntry != &IoTerminalList) {
- TerminalAfter = LIST_VALUE(CurrentEntry, TERMINAL, ListEntry);
- //
- // Assert that the list is in order.
- //
- ASSERT(TerminalAfter->Number >= Number);
- if (TerminalAfter->Number == Number) {
- Number += 1;
- } else {
- TerminalAfterEntry = CurrentEntry;
- break;
- }
- CurrentEntry = CurrentEntry->Next;
- }
- //
- // Create the terminal name string (on the stack, it gets copied by
- // the object manager) and then set the name in the object.
- //
- NameLength = RtlPrintToString(Name,
- TERMINAL_MAX_NAME_LENGTH,
- CharacterEncodingDefault,
- TERMINAL_MASTER_NAME_FORMAT,
- Number);
- Status = ObNameObject(Terminal,
- Name,
- NameLength,
- TERMINAL_ALLOCATION_TAG,
- FALSE);
- if (!KSUCCESS(Status)) {
- ASSERT(Status != STATUS_TOO_LATE);
- goto CreateTerminalEnd;
- }
- ASSERT(Terminal->Number == MAX_ULONG);
- Terminal->Number = Number;
- INSERT_BEFORE(&(Terminal->ListEntry), TerminalAfterEntry);
- KeReleaseQueuedLock(IoTerminalListLock);
- ListLockHeld = FALSE;
- }
- ASSERT((*FileObject)->Properties.Type == IoObjectTerminalMaster);
- Terminal->MasterFileObject = *FileObject;
- CreationParameters->Master = Terminal;
- ASSERT((*FileObject)->SpecialIo == NULL);
- (*FileObject)->SpecialIo = Terminal;
- }
- Status = STATUS_SUCCESS;
- CreateTerminalEnd:
- if (ListLockHeld != FALSE) {
- KeReleaseQueuedLock(IoTerminalListLock);
- }
- //
- // On both success and failure, the file object's ready event needs to be
- // signaled. Other threads may be waiting on the event.
- //
- if (*FileObject != NULL) {
- ASSERT((KeGetEventState((*FileObject)->ReadyEvent) == NotSignaled) ||
- (KeGetEventState((*FileObject)->ReadyEvent) ==
- NotSignaledWithWaiters));
- KeSignalEvent((*FileObject)->ReadyEvent, SignalOptionSignalAll);
- }
- return Status;
- }
- KSTATUS
- IopUnlinkTerminal (
- PFILE_OBJECT FileObject,
- PBOOL Unlinked
- )
- /*++
- Routine Description:
- This routine unlinks a terminal from the accessible namespace.
- Arguments:
- FileObject - Supplies a pointer to the terminal's file object.
- Unlinked - Supplies a pointer to a boolean that receives whether or not the
- terminal was successfully unlinked.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS Status;
- POBJECT_HEADER Terminal;
- ASSERT((FileObject->Properties.Type == IoObjectTerminalMaster) ||
- (FileObject->Properties.Type == IoObjectTerminalSlave));
- ASSERT(KeIsSharedExclusiveLockHeldExclusive(FileObject->Lock) != FALSE);
- Terminal = FileObject->SpecialIo;
- ASSERT(Terminal != NULL);
- *Unlinked = FALSE;
- Status = ObUnlinkObject(Terminal);
- if (KSUCCESS(Status)) {
- *Unlinked = TRUE;
- }
- return Status;
- }
- KSTATUS
- IopTerminalUserControl (
- PIO_HANDLE Handle,
- TERMINAL_USER_CONTROL_CODE CodeNumber,
- BOOL FromKernelMode,
- PVOID ContextBuffer,
- UINTN ContextBufferSize
- )
- /*++
- Routine Description:
- This routine handles user control requests destined for a terminal object.
- Arguments:
- Handle - Supplies the open file handle.
- CodeNumber - Supplies the minor code of the request.
- FromKernelMode - Supplies a boolean indicating whether or not this request
- (and the buffer associated with it) originates from user mode (FALSE)
- or kernel mode (TRUE).
- ContextBuffer - Supplies a pointer to the context buffer allocated by the
- caller for the request.
- ContextBufferSize - Supplies the size of the supplied context buffer.
- Return Value:
- Status code.
- --*/
- {
- BOOL AcceptingSignal;
- INT Argument;
- IO_CONTEXT Context;
- PROCESS_GROUP_ID CurrentProcessGroupId;
- SESSION_ID CurrentSessionId;
- PFILE_OBJECT FileObject;
- KSTATUS HardwareStatus;
- UINTN Index;
- BOOL InSession;
- IO_BUFFER IoBuffer;
- ULONG IoBufferFlags;
- INT ModemStatus;
- TERMINAL_SETTINGS_OLD OldSettings;
- PKPROCESS Process;
- PROCESS_GROUP_ID ProcessGroupId;
- INT QueueSize;
- SESSION_ID SessionId;
- TERMINAL_SETTINGS Settings;
- KSTATUS Status;
- PTERMINAL Terminal;
- PTERMINAL_SLAVE TerminalSlave;
- TERMINAL_CHANGE_BEHAVIOR When;
- TERMINAL_WINDOW_SIZE WindowSize;
- TerminalSlave = NULL;
- FileObject = Handle->FileObject;
- if (FileObject->Properties.Type == IoObjectTerminalMaster) {
- Terminal = FileObject->SpecialIo;
- } else if (FileObject->Properties.Type == IoObjectTerminalSlave) {
- TerminalSlave = FileObject->SpecialIo;
- Terminal = TerminalSlave->Master;
- } else {
- return STATUS_NOT_A_TERMINAL;
- }
- switch (CodeNumber) {
- case TerminalControlGetAttributes:
- Status = IoSetTerminalSettings(Handle,
- NULL,
- &Settings,
- TerminalChangeNone);
- if (!KSUCCESS(Status)) {
- break;
- }
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- FALSE,
- ContextBuffer,
- &Settings,
- sizeof(TERMINAL_SETTINGS));
- break;
- case TerminalControlSetAttributes:
- case TerminalControlSetAttributesDrain:
- case TerminalControlSetAttributesFlush:
- if (CodeNumber == TerminalControlSetAttributes) {
- When = TerminalChangeNow;
- } else if (CodeNumber == TerminalControlSetAttributesDrain) {
- When = TerminalChangeAfterOutput;
- } else {
- ASSERT(CodeNumber == TerminalControlSetAttributesFlush);
- When = TerminalChangeAfterOutputFlushInput;
- }
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- TRUE,
- ContextBuffer,
- &Settings,
- sizeof(TERMINAL_SETTINGS));
- if (!KSUCCESS(Status)) {
- break;
- }
- Status = IoSetTerminalSettings(Handle, &Settings, NULL, When);
- break;
- case TerminalControlGetAttributesOld:
- Status = IoSetTerminalSettings(Handle,
- NULL,
- &Settings,
- TerminalChangeNone);
- if (!KSUCCESS(Status)) {
- break;
- }
- RtlZeroMemory(&OldSettings, sizeof(TERMINAL_SETTINGS_OLD));
- OldSettings.InputFlags = Settings.InputFlags;
- OldSettings.OutputFlags = Settings.OutputFlags;
- OldSettings.ControlFlags = Settings.ControlFlags;
- OldSettings.LocalFlags = Settings.LocalFlags;
- OldSettings.LineDiscipline = 0;
- for (Index = 0;
- Index < TERMINAL_SETTINGS_OLD_CONTROL_COUNT;
- Index += 1) {
- OldSettings.ControlCharacters[Index] =
- Settings.ControlCharacters[Index];
- }
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- FALSE,
- ContextBuffer,
- &OldSettings,
- sizeof(TERMINAL_SETTINGS_OLD));
- break;
- case TerminalControlSetAttributesOld:
- case TerminalControlSetAttributesDrainOld:
- case TerminalControlSetAttributesFlushOld:
- if (CodeNumber == TerminalControlSetAttributesOld) {
- When = TerminalChangeNow;
- } else if (CodeNumber == TerminalControlSetAttributesDrainOld) {
- When = TerminalChangeAfterOutput;
- } else {
- ASSERT(CodeNumber == TerminalControlSetAttributesFlushOld);
- When = TerminalChangeAfterOutputFlushInput;
- }
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- TRUE,
- ContextBuffer,
- &OldSettings,
- sizeof(TERMINAL_SETTINGS_OLD));
- if (!KSUCCESS(Status)) {
- break;
- }
- //
- // Get the current settings, and copy the old to the new.
- //
- Status = IoSetTerminalSettings(Handle,
- NULL,
- &Settings,
- TerminalChangeNone);
- if (!KSUCCESS(Status)) {
- break;
- }
- Settings.InputFlags = OldSettings.InputFlags;
- Settings.OutputFlags = OldSettings.OutputFlags;
- Settings.ControlFlags = OldSettings.ControlFlags;
- Settings.LocalFlags = OldSettings.LocalFlags;
- for (Index = 0;
- Index < TERMINAL_SETTINGS_OLD_CONTROL_COUNT;
- Index += 1) {
- Settings.ControlCharacters[Index] =
- OldSettings.ControlCharacters[Index];
- }
- //
- // Set the new settings.
- //
- Status = IoSetTerminalSettings(Handle, &Settings, NULL, When);
- break;
- case TerminalControlSendBreak:
- //
- // The integer argument is the pointer itself.
- //
- Argument = (UINTN)ContextBuffer;
- if (Argument == 0) {
- Status = STATUS_SUCCESS;
- } else {
- //
- // A non-zero argument is undefined. Act like "drain" here, and
- // wait for all output to complete.
- //
- Status = IopTerminalFlush(FileObject, FLUSH_FLAG_WRITE);
- }
- break;
- case TerminalControlFlowControl:
- Status = STATUS_SUCCESS;
- break;
- case TerminalControlFlush:
- //
- // The argument is an integer.
- //
- Argument = (UINTN)ContextBuffer;
- Argument &= FLUSH_FLAG_READ | FLUSH_FLAG_WRITE;
- Argument |= FLUSH_FLAG_DISCARD;
- Status = IopTerminalFlush(FileObject, Argument);
- break;
- case TerminalControlSetExclusive:
- case TerminalControlClearExclusive:
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- if (CodeNumber == TerminalControlSetExclusive) {
- Terminal->Flags |= TERMINAL_FLAG_FAIL_OPENS;
- } else {
- Terminal->Flags &= ~TERMINAL_FLAG_FAIL_OPENS;
- }
- KeReleaseQueuedLock(Terminal->InputLock);
- KeReleaseQueuedLock(Terminal->OutputLock);
- Status = STATUS_SUCCESS;
- break;
- case TerminalControlGetOutputQueueSize:
- case TerminalControlGetInputQueueSize:
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- if (CodeNumber == TerminalControlGetOutputQueueSize) {
- QueueSize = IopTerminalGetOutputBufferSpace(Terminal) -
- TERMINAL_OUTPUT_BUFFER_SIZE;
- } else {
- QueueSize = IopTerminalGetInputBufferSpace(Terminal) -
- TERMINAL_INPUT_BUFFER_SIZE;
- }
- KeReleaseQueuedLock(Terminal->InputLock);
- KeReleaseQueuedLock(Terminal->OutputLock);
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- FALSE,
- ContextBuffer,
- &QueueSize,
- sizeof(INT));
- break;
- case TerminalControlInsertInInputQueue:
- IoBufferFlags = 0;
- if (FromKernelMode != FALSE) {
- IoBufferFlags |= IO_BUFFER_FLAG_KERNEL_MODE_DATA;
- }
- Status = MmInitializeIoBuffer(&IoBuffer,
- ContextBuffer,
- INVALID_PHYSICAL_ADDRESS,
- 1,
- IoBufferFlags);
- if (!KSUCCESS(Status)) {
- break;
- }
- Context.IoBuffer = &IoBuffer;
- Context.SizeInBytes = 1;
- Context.Flags = 0;
- Context.TimeoutInMilliseconds = WAIT_TIME_INDEFINITE;
- Status = IopTerminalMasterWrite(Terminal->MasterFileObject, &Context);
- break;
- case TerminalControlGetWindowSize:
- KeAcquireQueuedLock(Terminal->OutputLock);
- RtlCopyMemory(&WindowSize,
- &(Terminal->WindowSize),
- sizeof(TERMINAL_WINDOW_SIZE));
- KeReleaseQueuedLock(Terminal->OutputLock);
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- FALSE,
- ContextBuffer,
- &WindowSize,
- sizeof(TERMINAL_WINDOW_SIZE));
- break;
- case TerminalControlSetWindowSize:
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- TRUE,
- ContextBuffer,
- &WindowSize,
- sizeof(TERMINAL_WINDOW_SIZE));
- if (!KSUCCESS(Status)) {
- break;
- }
- KeAcquireQueuedLock(Terminal->OutputLock);
- RtlCopyMemory(&(Terminal->WindowSize),
- &WindowSize,
- sizeof(TERMINAL_WINDOW_SIZE));
- KeReleaseQueuedLock(Terminal->OutputLock);
- break;
- case TerminalControlGetModemStatus:
- case TerminalControlOrModemStatus:
- case TerminalControlClearModemStatus:
- case TerminalControlSetModemStatus:
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- TRUE,
- ContextBuffer,
- &ModemStatus,
- sizeof(INT));
- if (!KSUCCESS(Status)) {
- break;
- }
- KeAcquireQueuedLock(Terminal->OutputLock);
- if (CodeNumber == TerminalControlOrModemStatus) {
- Terminal->ModemStatus |= ModemStatus;
- } else if (CodeNumber == TerminalControlClearModemStatus) {
- Terminal->ModemStatus &= ~ModemStatus;
- } else if (CodeNumber == TerminalControlSetModemStatus) {
- Terminal->ModemStatus = ModemStatus;
- }
- ModemStatus = Terminal->ModemStatus;
- KeReleaseQueuedLock(Terminal->OutputLock);
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- FALSE,
- ContextBuffer,
- &ModemStatus,
- sizeof(INT));
- break;
- case TerminalControlGetSoftCarrier:
- case TerminalControlSetSoftCarrier:
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- TRUE,
- ContextBuffer,
- &Argument,
- sizeof(INT));
- if (!KSUCCESS(Status)) {
- break;
- }
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- if (CodeNumber == TerminalControlSetSoftCarrier) {
- Terminal->ModemStatus |= ModemStatus;
- if (Argument != 0) {
- Terminal->Settings.ControlFlags |= TERMINAL_CONTROL_NO_HANGUP;
- } else {
- Terminal->Settings.ControlFlags &= ~TERMINAL_CONTROL_NO_HANGUP;
- }
- }
- Argument = FALSE;
- if ((Terminal->Settings.ControlFlags & TERMINAL_CONTROL_NO_HANGUP) !=
- 0) {
- Argument = TRUE;
- }
- KeReleaseQueuedLock(Terminal->InputLock);
- KeReleaseQueuedLock(Terminal->OutputLock);
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- FALSE,
- ContextBuffer,
- &Argument,
- sizeof(INT));
- break;
- case TerminalControlGetProcessGroup:
- //
- // The given terminal must be the controlling terminal of the calling
- // process.
- //
- PsGetProcessGroup(NULL, &CurrentProcessGroupId, &CurrentSessionId);
- Status = STATUS_SUCCESS;
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- if (Terminal->SessionId != CurrentSessionId) {
- Status = STATUS_NOT_A_TERMINAL;
- } else {
- ProcessGroupId = Terminal->ProcessGroupId;
- }
- KeReleaseQueuedLock(Terminal->InputLock);
- KeReleaseQueuedLock(Terminal->OutputLock);
- if (!KSUCCESS(Status)) {
- break;
- }
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- FALSE,
- ContextBuffer,
- &ProcessGroupId,
- sizeof(PROCESS_GROUP_ID));
- break;
- case TerminalControlSetProcessGroup:
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- TRUE,
- ContextBuffer,
- &ProcessGroupId,
- sizeof(PROCESS_GROUP_ID));
- if (!KSUCCESS(Status)) {
- break;
- }
- //
- // If the terminal does not have the slave side attached or does not
- // belong to the calling session, then the process does not have
- // permission to update its process group.
- //
- PsGetProcessGroup(NULL, &CurrentProcessGroupId, &CurrentSessionId);
- //
- // The given terminal must be in the current session.
- //
- InSession = PsIsProcessGroupInSession(ProcessGroupId, CurrentSessionId);
- if (InSession == FALSE) {
- Status = STATUS_PERMISSION_DENIED;
- break;
- }
- AcceptingSignal = FALSE;
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- if (Terminal->SessionId != CurrentSessionId) {
- Status = STATUS_NOT_A_TERMINAL;
- //
- // If the calling process is not in the owning (foreground) process
- // group, then it is sent a signal unless it is blocking or ignoring
- // the background terminal output signal.
- //
- } else {
- if (CurrentProcessGroupId != Terminal->ProcessGroupId) {
- AcceptingSignal = PsIsThreadAcceptingSignal(
- NULL,
- SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
- }
- //
- // If the process is not accepting signals or the signal did not
- // need to be checked, then the process group is free to change.
- //
- if (AcceptingSignal == FALSE) {
- Terminal->ProcessGroupId = ProcessGroupId;
- Status = STATUS_SUCCESS;
- }
- }
- KeReleaseQueuedLock(Terminal->InputLock);
- KeReleaseQueuedLock(Terminal->OutputLock);
- //
- // If the process is accepting the signal checked above, send it and
- // tell the caller to try again later. If it's not accepting it, just
- // let it go through.
- //
- if (AcceptingSignal != FALSE) {
- PsSignalProcessGroup(CurrentProcessGroupId,
- SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
- Status = STATUS_TRY_AGAIN;
- }
- break;
- case TerminalControlSetControllingTerminal:
- Argument = (UINTN)ContextBuffer;
- Process = PsGetCurrentProcess();
- //
- // If this process is not a session leader or it has a controlling
- // terminal already, fail.
- //
- if ((PsIsSessionLeader(Process) == FALSE) ||
- (Process->ControllingTerminal != NULL)) {
- Status = STATUS_PERMISSION_DENIED;
- }
- //
- // If this handle is only open for write and the caller isn't an
- // administrator, fail.
- //
- if ((Handle->Access & IO_ACCESS_READ) == 0) {
- Status = PsCheckPermission(PERMISSION_SYSTEM_ADMINISTRATOR);
- if (!KSUCCESS(Status)) {
- break;
- }
- }
- //
- // If the terminal already belongs to a different session, then it
- // cannot bet set as the controlling terminal of this session unless
- // the caller is root and the argument is 1.
- //
- SessionId = Terminal->SessionId;
- CurrentSessionId = Process->Identifiers.SessionId;
- if (SessionId != TERMINAL_INVALID_SESSION) {
- if (SessionId == CurrentSessionId) {
- Status = STATUS_SUCCESS;
- break;
- }
- //
- // Allow root to steal terminals from different session if the
- // argument is non-zero.
- //
- Status = PsCheckPermission(PERMISSION_SYSTEM_ADMINISTRATOR);
- if ((!KSUCCESS(Status)) || (Argument == 0)) {
- Status = STATUS_PERMISSION_DENIED;
- break;
- }
- }
- KeAcquireQueuedLock(IoTerminalListLock);
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- //
- // Double check the controlling terminal now that the terminal list
- // lock protecting it is held.
- //
- if (Process->ControllingTerminal != NULL) {
- Status = STATUS_PERMISSION_DENIED;
- //
- // If the session changed between the unlocked check and now, fail.
- //
- } else if (Terminal->SessionId != SessionId) {
- Status = STATUS_TRY_AGAIN;
- //
- // Everyone that had the terminal as their controlling terminal no
- // longer does.
- //
- } else {
- IopTerminalDisassociate(Terminal);
- Process->ControllingTerminal = Handle;
- Terminal->SessionId = CurrentSessionId;
- Terminal->ProcessGroupId = Process->Identifiers.ProcessGroupId;
- }
- KeReleaseQueuedLock(Terminal->InputLock);
- KeReleaseQueuedLock(Terminal->OutputLock);
- KeReleaseQueuedLock(IoTerminalListLock);
- break;
- case TerminalControlGetCurrentSessionId:
- if (FileObject->Properties.Type != IoObjectTerminalMaster) {
- Status = STATUS_NOT_A_TERMINAL;
- break;
- }
- //
- // The given terminal must be the controlling terminal of the calling
- // process.
- //
- Process = PsGetCurrentProcess();
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- if (Terminal->SessionId != CurrentSessionId) {
- Status = STATUS_NOT_A_TERMINAL;
- } else {
- SessionId = Terminal->SessionId;
- Status = STATUS_SUCCESS;
- }
- KeReleaseQueuedLock(Terminal->InputLock);
- KeReleaseQueuedLock(Terminal->OutputLock);
- if (!KSUCCESS(Status)) {
- break;
- }
- Status = IopTerminalUserBufferCopy(FromKernelMode,
- FALSE,
- ContextBuffer,
- &SessionId,
- sizeof(SESSION_ID));
- break;
- case TerminalControlGiveUpControllingTerminal:
- Process = PsGetCurrentProcess();
- //
- // The controlling terminal is protected by the terminal list lock.
- //
- KeAcquireQueuedLock(IoTerminalListLock);
- KeAcquireQueuedLock(Terminal->OutputLock);
- KeAcquireQueuedLock(Terminal->InputLock);
- if (Process->ControllingTerminal != Handle) {
- Status = STATUS_NOT_A_TERMINAL;
- } else {
- Status = STATUS_SUCCESS;
- if (PsIsSessionLeader(Process) != FALSE) {
- PsSignalProcessGroup(Terminal->ProcessGroupId,
- SIGNAL_CONTROLLING_TERMINAL_CLOSED);
- PsSignalProcessGroup(Terminal->ProcessGroupId,
- SIGNAL_CONTINUE);
- IopTerminalDisassociate(Terminal);
- }
- }
- KeReleaseQueuedLock(Terminal->InputLock);
- KeReleaseQueuedLock(Terminal->OutputLock);
- KeReleaseQueuedLock(IoTerminalListLock);
- break;
- case TerminalControlRedirectLocalConsole:
- case TerminalControlSetPacketMode:
- ASSERT(FALSE);
- Status = STATUS_NOT_IMPLEMENTED;
- break;
- case TerminalControlSendBreakPosix:
- case TerminalControlStartBreak:
- case TerminalControlStopBreak:
- Status = STATUS_SUCCESS;
- break;
- default:
- Status = STATUS_NOT_SUPPORTED;
- break;
- }
- //
- // Also forward the request on to the physical device if there is one.
- //
- if ((KSUCCESS(Status)) && (Terminal->HardwareHandle != NULL)) {
- HardwareStatus = IoUserControl(Terminal->HardwareHandle,
- CodeNumber,
- FromKernelMode,
- ContextBuffer,
- ContextBufferSize);
- if (HardwareStatus != STATUS_NOT_HANDLED) {
- Status = HardwareStatus;
- }
- }
- return Status;
- }
- KSTATUS
- IopTerminalFlush (
- PFILE_OBJECT FileObject,
- ULONG Flags
- )
- /*++
- Routine Description:
- This routine flushes a terminal object, discarding unwritten and unread
- data.
- Arguments:
- FileObject - Supplies a pointer to the terminal to flush.
- Flags - Supplies the flags governing the flush operation. See FLUSH_FLAG_*
- definitions.
- Return Value:
- Status code.
- --*/
- {
- BOOL AcceptingSignal;
- PIO_OBJECT_STATE MasterIoState;
- PROCESS_GROUP_ID ProcessGroup;
- SESSION_ID Session;
- PTERMINAL_SLAVE Slave;
- PIO_OBJECT_STATE SlaveIoState;
- KSTATUS Status;
- PTERMINAL Terminal;
- if (FileObject->Properties.Type == IoObjectTerminalSlave) {
- Slave = FileObject->SpecialIo;
- Terminal = NULL;
- ASSERT(Slave->Header.Type == ObjectTerminalSlave);
- Terminal = Slave->Master;
- if (IO_IS_TERMINAL_MASTER_OPEN(Terminal) == FALSE) {
- Status = STATUS_END_OF_FILE;
- goto TerminalFlushEnd;
- }
- } else {
- Terminal = FileObject->SpecialIo;
- }
- if (Terminal->SlaveFileObject == NULL) {
- Status = STATUS_NOT_FOUND;
- goto TerminalFlushEnd;
- }
- SlaveIoState = Terminal->SlaveFileObject->IoState;
- MasterIoState = Terminal->MasterFileObject->IoState;
- PsGetProcessGroup(NULL, &ProcessGroup, &Session);
- if (Terminal->SlaveHandles == 0) {
- Status = STATUS_NOT_READY;
- goto TerminalFlushEnd;
- }
- //
- // If the flushing process is not in the same process group, send the
- // process group a signal unless the flushing process is ignoring or
- // blocking that signal.
- //
- if ((ProcessGroup != Terminal->ProcessGroupId) &&
- ((Terminal->Settings.LocalFlags &
- TERMINAL_LOCAL_STOP_BACKGROUND_WRITES) != 0)) {
- AcceptingSignal = PsIsThreadAcceptingSignal(
- NULL,
- SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
- //
- // If the process is accepting that signal, send it to it and tell it
- // to try again later. The exception is an orphaned process group, in
- // which case an error is returned. If the process is not accepting the
- // signal, just let the flush go through.
- //
- if (AcceptingSignal != FALSE) {
- if (PsIsProcessGroupOrphaned(ProcessGroup) != FALSE) {
- Status = STATUS_DEVICE_IO_ERROR;
- } else {
- PsSignalProcessGroup(ProcessGroup,
- SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
- Status = STATUS_TRY_AGAIN;
- }
- goto TerminalFlushEnd;
- }
- }
- //
- // If discarding, reset the buffers.
- //
- if ((Flags & FLUSH_FLAG_DISCARD) != 0) {
- if ((Flags & FLUSH_FLAG_READ) != 0) {
- KeAcquireQueuedLock(Terminal->InputLock);
- Terminal->InputBufferStart = 0;
- Terminal->InputBufferEnd = 0;
- IoSetIoObjectState(MasterIoState, POLL_EVENT_OUT, TRUE);
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, FALSE);
- Terminal->WorkingInputCursor = 0;
- Terminal->WorkingInputLength = 0;
- KeReleaseQueuedLock(Terminal->InputLock);
- }
- if ((Flags & FLUSH_FLAG_WRITE) != 0) {
- KeAcquireQueuedLock(Terminal->OutputLock);
- Terminal->OutputBufferStart = 0;
- Terminal->OutputBufferEnd = 0;
- IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, FALSE);
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, TRUE);
- KeReleaseQueuedLock(Terminal->OutputLock);
- }
- //
- // If draining, wait for the output to go through.
- //
- } else {
- //
- // It doesn't make sense for the caller to try to flush a read, as
- // they're the ones that need to flush.
- //
- if ((Flags & FLUSH_FLAG_READ) != 0) {
- Status = STATUS_INVALID_PARAMETER;
- goto TerminalFlushEnd;
- }
- //
- // Wait for the write buffer to become empty.
- //
- if ((Flags & FLUSH_FLAG_WRITE) != 0) {
- Status = STATUS_SUCCESS;
- while (KSUCCESS(Status)) {
- KeAcquireQueuedLock(Terminal->OutputLock);
- //
- // If the output is empty, then hooray, it's done.
- //
- if (Terminal->OutputBufferStart == Terminal->OutputBufferEnd) {
- KeReleaseQueuedLock(Terminal->OutputLock);
- break;
- }
- //
- // Hijack the out event and unsignal it. When the master reads
- // the data, it will signal it again.
- //
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, FALSE);
- KeReleaseQueuedLock(Terminal->OutputLock);
- Status = KeWaitForEvent(SlaveIoState->WriteEvent,
- TRUE,
- WAIT_TIME_INDEFINITE);
- }
- if (!KSUCCESS(Status)) {
- goto TerminalFlushEnd;
- }
- }
- }
- Status = STATUS_SUCCESS;
- TerminalFlushEnd:
- return Status;
- }
- //
- // --------------------------------------------------------- Internal Functions
- //
- KSTATUS
- IopCreateTerminalObject (
- FILE_PERMISSIONS CreatePermissions,
- PTERMINAL *NewTerminal
- )
- /*++
- Routine Description:
- This routine creates a new terminal object.
- Arguments:
- CreatePermissions - Supplies the initial permissions to set on the slave
- file object.
- NewTerminal - Supplies a pointer where a pointer to a new terminal will be
- returned on success.
- Return Value:
- Status code.
- --*/
- {
- PCHAR ControlCharacters;
- PTERMINAL_SLAVE Slave;
- KSTATUS Status;
- PTERMINAL Terminal;
- //
- // Create the terminal object. This references goes to the special I/O
- // member of the file object on success.
- //
- Terminal = ObCreateObject(ObjectTerminalMaster,
- IoTerminalDirectory,
- NULL,
- 0,
- sizeof(TERMINAL),
- IopDestroyTerminal,
- 0,
- TERMINAL_ALLOCATION_TAG);
- if (Terminal == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateTerminalObjectEnd;
- }
- //
- // Initialize the terminal with an invalid number. The number is only used
- // by terminals that are named in the terminal directory. Naming a terminal
- // happens later with the appropriate synchronization.
- //
- Terminal->Number = MAX_ULONG;
- //
- // Set the master reference count to 1. This helps determine when the
- // master is last closed by preventing new opens from succeeding if the
- // master's reference goes to 0.
- //
- Terminal->MasterReferenceCount = 1;
- //
- // Allocate the input buffers.
- //
- Terminal->InputBuffer = MmAllocatePagedPool(TERMINAL_INPUT_BUFFER_SIZE,
- TERMINAL_ALLOCATION_TAG);
- if (Terminal->InputBuffer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateTerminalObjectEnd;
- }
- RtlZeroMemory(Terminal->InputBuffer, TERMINAL_INPUT_BUFFER_SIZE);
- Terminal->WorkingInputBuffer = MmAllocatePagedPool(
- TERMINAL_CANONICAL_BUFFER_SIZE,
- TERMINAL_ALLOCATION_TAG);
- if (Terminal->WorkingInputBuffer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateTerminalObjectEnd;
- }
- Terminal->InputLock = KeCreateQueuedLock();
- if (Terminal->InputLock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateTerminalObjectEnd;
- }
- //
- // Allocate the output buffers.
- //
- Terminal->OutputBuffer = MmAllocatePagedPool(TERMINAL_OUTPUT_BUFFER_SIZE,
- TERMINAL_ALLOCATION_TAG);
- if (Terminal->OutputBuffer == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateTerminalObjectEnd;
- }
- Terminal->OutputLock = KeCreateQueuedLock();
- if (Terminal->OutputLock == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateTerminalObjectEnd;
- }
- //
- // Set some default flags.
- //
- Terminal->Settings.LocalFlags = TERMINAL_LOCAL_ECHO |
- TERMINAL_LOCAL_ECHO_ERASE |
- TERMINAL_LOCAL_ECHO_KILL_NEWLINE |
- TERMINAL_LOCAL_ECHO_KILL_EXTENDED |
- TERMINAL_LOCAL_ECHO_NEWLINE |
- TERMINAL_LOCAL_ECHO_CONTROL |
- TERMINAL_LOCAL_CANONICAL |
- TERMINAL_LOCAL_SIGNALS;
- Terminal->Settings.InputFlags = TERMINAL_INPUT_CR_TO_NEWLINE;
- Terminal->Settings.OutputFlags = TERMINAL_OUTPUT_POST_PROCESS |
- TERMINAL_OUTPUT_NEWLINE_TO_CRLF;
- Terminal->Settings.ControlFlags = TERMINAL_CONTROL_8_BITS_PER_CHARACTER;
- ControlCharacters = Terminal->Settings.ControlCharacters;
- ControlCharacters[TerminalCharacterEndOfFile] =
- TERMINAL_DEFAULT_END_OF_FILE;
- ControlCharacters[TerminalCharacterEndOfLine] =
- TERMINAL_DEFAULT_END_OF_LINE;
- ControlCharacters[TerminalCharacterErase] = TERMINAL_DEFAULT_ERASE;
- ControlCharacters[TerminalCharacterInterrupt] = TERMINAL_DEFAULT_INTERRUPT;
- ControlCharacters[TerminalCharacterKill] = TERMINAL_DEFAULT_KILL;
- ControlCharacters[TerminalCharacterQuit] = TERMINAL_DEFAULT_QUIT;
- ControlCharacters[TerminalCharacterSuspend] = TERMINAL_DEFAULT_SUSPEND;
- ControlCharacters[TerminalCharacterStart] = TERMINAL_DEFAULT_START;
- ControlCharacters[TerminalCharacterStop] = TERMINAL_DEFAULT_STOP;
- ControlCharacters[TerminalCharacterFlushCount] = 1;
- ControlCharacters[TerminalCharacterFlushTime] = 0;
- Terminal->Settings.InputSpeed = TERMINAL_DEFAULT_BAUD_RATE;
- Terminal->Settings.OutputSpeed = TERMINAL_DEFAULT_BAUD_RATE;
- Terminal->WindowSize.Rows = TERMINAL_DEFAULT_ROWS;
- Terminal->WindowSize.Columns = TERMINAL_DEFAULT_COLUMNS;
- //
- // Initialize the owning session and process group.
- //
- Terminal->SessionId = TERMINAL_INVALID_SESSION;
- Terminal->ProcessGroupId = TERMINAL_INVALID_PROCESS_GROUP;
- //
- // Create the corresponding slave object.
- //
- Status = STATUS_SUCCESS;
- Slave = ObCreateObject(ObjectTerminalSlave,
- IoTerminalDirectory,
- NULL,
- 0,
- sizeof(TERMINAL_SLAVE),
- NULL,
- 0,
- TERMINAL_ALLOCATION_TAG);
- if (Slave == NULL) {
- Status = STATUS_INSUFFICIENT_RESOURCES;
- goto CreateTerminalObjectEnd;
- }
- //
- // Wire the master and slave together.
- //
- Terminal->Slave = Slave;
- Slave->Master = Terminal;
- //
- // Add the terminal to the end of the list.
- //
- KeAcquireQueuedLock(IoTerminalListLock);
- INSERT_BEFORE(&(Terminal->ListEntry), &IoTerminalList);
- KeReleaseQueuedLock(IoTerminalListLock);
- Status = STATUS_SUCCESS;
- CreateTerminalObjectEnd:
- if (!KSUCCESS(Status)) {
- if (Terminal != NULL) {
- ObReleaseReference(Terminal);
- Terminal = NULL;
- }
- }
- *NewTerminal = Terminal;
- return Status;
- }
- VOID
- IopDestroyTerminal (
- PVOID TerminalObject
- )
- /*++
- Routine Description:
- This routine is called when an terminal master's reference count drops to
- zero. It destroys all resources associated with the terminal. This occurs
- well after all the slave has been freed.
- Arguments:
- TerminalObject - Supplies a pointer to the terminal object being destroyed.
- Return Value:
- None.
- --*/
- {
- PTERMINAL Terminal;
- Terminal = (PTERMINAL)TerminalObject;
- ASSERT(Terminal->SlavePathPoint.PathEntry == NULL);
- //
- // If the slave never got a file object, then the master still has a
- // reference on the slave it needs to release.
- //
- if (Terminal->SlaveFileObject == NULL) {
- ObReleaseReference(Terminal->Slave);
- }
- if (Terminal->ListEntry.Next != NULL) {
- KeAcquireQueuedLock(IoTerminalListLock);
- LIST_REMOVE(&(Terminal->ListEntry));
- KeReleaseQueuedLock(IoTerminalListLock);
- }
- if (Terminal->HardwareHandle != NULL) {
- IoClose(Terminal->HardwareHandle);
- Terminal->HardwareHandle = NULL;
- }
- if (Terminal->InputBuffer != NULL) {
- MmFreePagedPool(Terminal->InputBuffer);
- }
- if (Terminal->WorkingInputBuffer != NULL) {
- MmFreePagedPool(Terminal->WorkingInputBuffer);
- }
- if (Terminal->InputLock != NULL) {
- KeDestroyQueuedLock(Terminal->InputLock);
- }
- if (Terminal->OutputBuffer != NULL) {
- MmFreePagedPool(Terminal->OutputBuffer);
- }
- if (Terminal->OutputLock != NULL) {
- KeDestroyQueuedLock(Terminal->OutputLock);
- }
- return;
- }
- KSTATUS
- IopTerminalMasterWrite (
- PFILE_OBJECT FileObject,
- PIO_CONTEXT IoContext
- )
- /*++
- Routine Description:
- This routine writes data to the terminal slave (data that will come out
- the slave's standard input).
- Arguments:
- FileObject - Supplies a pointer to the terminal master file object.
- IoContext - Supplies a pointer to the I/O context.
- Return Value:
- Status code. A failing status code does not necessarily mean no I/O made it
- in or out. Check the bytes completed value in the I/O context to find out
- how much occurred.
- --*/
- {
- BOOL AddCharacter;
- UCHAR Byte;
- UINTN ByteIndex;
- UCHAR Bytes[2];
- UINTN BytesSize;
- BOOL CharacterHandled;
- PCHAR ControlCharacters;
- ULONG DirtyRegionBegin;
- ULONG DirtyRegionEnd;
- ULONG EchoFlags;
- ULONG EchoMask;
- BOOL EchoThisCharacter;
- BOOL InputAdded;
- ULONG InputFlags;
- BOOL InputLockHeld;
- BOOL IsEndOfLine;
- UINTN LocalByteIndex;
- CHAR LocalBytes[64];
- UINTN LocalByteSize;
- ULONG LocalFlags;
- PIO_OBJECT_STATE MasterIoState;
- ULONG MoveIndex;
- BOOL OutputLockHeld;
- BOOL OutputWritten;
- ULONG ReturnedEvents;
- ULONG ScreenCursorPosition;
- PIO_OBJECT_STATE SlaveIoState;
- ULONG Space;
- KSTATUS Status;
- PTERMINAL Terminal;
- ULONG TimeoutInMilliseconds;
- BOOL TransferWorkingBuffer;
- Terminal = FileObject->SpecialIo;
- ASSERT(Terminal->Header.Type == ObjectTerminalMaster);
- ASSERT(FileObject == Terminal->MasterFileObject);
- MasterIoState = FileObject->IoState;
- if (Terminal->SlaveFileObject == NULL) {
- IoContext->BytesCompleted = 0;
- return STATUS_NOT_READY;
- }
- SlaveIoState = Terminal->SlaveFileObject->IoState;
- InputFlags = Terminal->Settings.InputFlags;
- LocalFlags = Terminal->Settings.LocalFlags;
- EchoMask = TERMINAL_LOCAL_ECHO | TERMINAL_LOCAL_ECHO_ERASE |
- TERMINAL_LOCAL_ECHO_KILL_NEWLINE |
- TERMINAL_LOCAL_ECHO_KILL_EXTENDED |
- TERMINAL_LOCAL_ECHO_NEWLINE |
- TERMINAL_LOCAL_ECHO_CONTROL;
- EchoFlags = LocalFlags & EchoMask;
- ControlCharacters = Terminal->Settings.ControlCharacters;
- InputAdded = FALSE;
- DirtyRegionBegin = Terminal->WorkingInputCursor;
- DirtyRegionEnd = Terminal->WorkingInputCursor;
- OutputLockHeld = FALSE;
- OutputWritten = FALSE;
- ScreenCursorPosition = Terminal->WorkingInputCursor;
- TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
- if (EchoFlags != 0) {
- KeAcquireQueuedLock(Terminal->OutputLock);
- OutputLockHeld = TRUE;
- }
- KeAcquireQueuedLock(Terminal->InputLock);
- InputLockHeld = TRUE;
- //
- // Loop through every byte.
- //
- LocalByteIndex = 0;
- LocalByteSize = 0;
- for (ByteIndex = 0; ByteIndex < IoContext->SizeInBytes; ByteIndex += 1) {
- TransferWorkingBuffer = FALSE;
- AddCharacter = TRUE;
- //
- // Get the particular byte in question. Keep a local bounce buffer to
- // avoid calling the copy I/O buffer data function for every single
- // byte.
- //
- if (LocalByteIndex < LocalByteSize) {
- Byte = LocalBytes[LocalByteIndex];
- LocalByteIndex += 1;
- } else {
- LocalByteSize = IoContext->SizeInBytes - ByteIndex;
- if (LocalByteSize > sizeof(LocalBytes)) {
- LocalByteSize = sizeof(LocalBytes);
- }
- Status = MmCopyIoBufferData(IoContext->IoBuffer,
- LocalBytes,
- ByteIndex,
- LocalByteSize,
- FALSE);
- if (!KSUCCESS(Status)) {
- goto TerminalMasterWriteEnd;
- }
- Byte = LocalBytes[0];
- LocalByteIndex = 1;
- }
- //
- // Grab the output lock if it's not already held.
- //
- if ((OutputLockHeld == FALSE) && (EchoFlags != 0)) {
- KeAcquireQueuedLock(Terminal->OutputLock);
- OutputLockHeld = TRUE;
- }
- //
- // Process signal generating characters.
- //
- if (Byte == ControlCharacters[TerminalCharacterInterrupt]) {
- if ((LocalFlags & TERMINAL_LOCAL_SIGNALS) != 0) {
- AddCharacter = FALSE;
- if ((Terminal->SlaveHandles != 0) &&
- (Terminal->ProcessGroupId !=
- TERMINAL_INVALID_PROCESS_GROUP)) {
- PsSignalProcessGroup(Terminal->ProcessGroupId,
- SIGNAL_KEYBOARD_INTERRUPT);
- }
- }
- }
- if (Byte == ControlCharacters[TerminalCharacterQuit]) {
- if ((LocalFlags & TERMINAL_LOCAL_SIGNALS) != 0) {
- AddCharacter = FALSE;
- if ((Terminal->SlaveHandles != 0) &&
- (Terminal->ProcessGroupId !=
- TERMINAL_INVALID_PROCESS_GROUP)) {
- PsSignalProcessGroup(Terminal->ProcessGroupId,
- SIGNAL_REQUEST_CORE_DUMP);
- }
- }
- }
- //
- // Run through the input flags.
- //
- if ((InputFlags & TERMINAL_INPUT_STRIP) != 0) {
- Byte &= 0x7F;
- }
- if (Byte == '\r') {
- if ((InputFlags & TERMINAL_INPUT_CR_TO_NEWLINE) != 0) {
- Byte = '\n';
- } else if ((InputFlags & TERMINAL_INPUT_IGNORE_CR) != 0) {
- AddCharacter = FALSE;
- }
- } else if (Byte == '\n') {
- if ((InputFlags & TERMINAL_INPUT_NEWLINE_TO_CR) != 0) {
- Byte = '\r';
- }
- }
- //
- // Process the byte in cooked mode.
- //
- if ((LocalFlags & TERMINAL_LOCAL_CANONICAL) != 0) {
- IsEndOfLine = FALSE;
- //
- // First let an editing function take a look at it.
- //
- CharacterHandled = IopTerminalProcessEditingCharacter(
- Terminal,
- Byte,
- TimeoutInMilliseconds,
- &DirtyRegionBegin,
- &DirtyRegionEnd,
- &ScreenCursorPosition,
- &OutputWritten);
- if (CharacterHandled != FALSE) {
- AddCharacter = FALSE;
- //
- // Pushing return transfers the working buffer to the slave's input.
- //
- } else if ((Byte ==
- ControlCharacters[TerminalCharacterEndOfLine]) ||
- (Byte == '\n')) {
- TransferWorkingBuffer = TRUE;
- Terminal->WorkingInputCursor = Terminal->WorkingInputLength;
- IsEndOfLine = TRUE;
- //
- // End of file also causes output to be flushed.
- //
- } else if (Byte == ControlCharacters[TerminalCharacterEndOfFile]) {
- TransferWorkingBuffer = TRUE;
- }
- //
- // Add the character to the working buffer if needed.
- //
- if (AddCharacter != FALSE) {
- if (Terminal->WorkingInputLength !=
- TERMINAL_CANONICAL_BUFFER_SIZE) {
- if (Terminal->WorkingInputCursor < DirtyRegionBegin) {
- DirtyRegionBegin = Terminal->WorkingInputCursor;
- }
- //
- // Make a hole.
- //
- for (MoveIndex = Terminal->WorkingInputLength;
- MoveIndex > Terminal->WorkingInputCursor;
- MoveIndex -= 1) {
- Terminal->WorkingInputBuffer[MoveIndex] =
- Terminal->WorkingInputBuffer[MoveIndex - 1];
- }
- Terminal->WorkingInputBuffer[Terminal->WorkingInputCursor] =
- Byte;
- Terminal->WorkingInputCursor += 1;
- Terminal->WorkingInputLength += 1;
- if ((IsEndOfLine == FALSE) &&
- (Terminal->WorkingInputLength > DirtyRegionEnd)) {
- DirtyRegionEnd = Terminal->WorkingInputLength;
- Terminal->Flags &= ~(TERMINAL_FLAG_VIRGIN_LINE |
- TERMINAL_FLAG_UNEDITED_LINE);
- }
- }
- }
- //
- // Flush the buffer if desired.
- //
- if (TransferWorkingBuffer != FALSE) {
- //
- // Fix up the line before abandoning it.
- //
- if ((DirtyRegionBegin != DirtyRegionEnd) &&
- ((EchoFlags & TERMINAL_LOCAL_ECHO) != 0)) {
- if (OutputLockHeld == FALSE) {
- KeAcquireQueuedLock(Terminal->OutputLock);
- OutputLockHeld = TRUE;
- }
- IopTerminalFixUpCanonicalLine(Terminal,
- TimeoutInMilliseconds,
- DirtyRegionBegin,
- DirtyRegionEnd,
- ScreenCursorPosition);
- ScreenCursorPosition = Terminal->WorkingInputCursor;
- OutputWritten = TRUE;
- }
- //
- // Wait for there to be enough space.
- //
- while (TRUE) {
- if (InputLockHeld == FALSE) {
- KeAcquireQueuedLock(Terminal->InputLock);
- InputLockHeld = TRUE;
- }
- InputFlags = Terminal->Settings.InputFlags;
- LocalFlags = Terminal->Settings.LocalFlags;
- EchoFlags = LocalFlags & EchoMask;
- Space = IopTerminalGetInputBufferSpace(Terminal);
- if (Space >= Terminal->WorkingInputLength) {
- break;
- }
- IoSetIoObjectState(MasterIoState, POLL_EVENT_OUT, FALSE);
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, TRUE);
- InputAdded = FALSE;
- KeReleaseQueuedLock(Terminal->InputLock);
- InputLockHeld = FALSE;
- Status = IoWaitForIoObjectState(MasterIoState,
- POLL_EVENT_OUT,
- TRUE,
- TimeoutInMilliseconds,
- &ReturnedEvents);
- if (!KSUCCESS(Status)) {
- goto TerminalMasterWriteEnd;
- }
- if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
- Status = STATUS_DEVICE_IO_ERROR;
- goto TerminalMasterWriteEnd;
- }
- }
- //
- // Move the bytes to the input buffer.
- //
- for (MoveIndex = 0;
- MoveIndex < Terminal->WorkingInputLength;
- MoveIndex += 1) {
- Terminal->InputBuffer[Terminal->InputBufferEnd] =
- Terminal->WorkingInputBuffer[MoveIndex];
- Terminal->InputBufferEnd += 1;
- if (Terminal->InputBufferEnd ==
- TERMINAL_INPUT_BUFFER_SIZE) {
- Terminal->InputBufferEnd = 0;
- }
- ASSERT(Terminal->InputBufferEnd !=
- Terminal->InputBufferStart);
- }
- KeReleaseQueuedLock(Terminal->InputLock);
- InputLockHeld = FALSE;
- InputAdded = TRUE;
- Terminal->WorkingInputCursor = 0;
- Terminal->WorkingInputLength = 0;
- DirtyRegionBegin = 0;
- DirtyRegionEnd = 0;
- ScreenCursorPosition = 0;
- Terminal->Flags |= TERMINAL_FLAG_VIRGIN_LINE |
- TERMINAL_FLAG_UNEDITED_LINE;
- }
- //
- // Input is not canonical, it just goes directly in the input buffer.
- //
- } else {
- if (AddCharacter == FALSE) {
- continue;
- }
- //
- // Wait if there's not enough space available.
- //
- while (IopTerminalGetInputBufferSpace(Terminal) == 0) {
- IoSetIoObjectState(MasterIoState, POLL_EVENT_OUT, FALSE);
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, TRUE);
- KeReleaseQueuedLock(Terminal->InputLock);
- InputLockHeld = FALSE;
- InputAdded = FALSE;
- Status = IoWaitForIoObjectState(MasterIoState,
- POLL_EVENT_OUT,
- TRUE,
- TimeoutInMilliseconds,
- &ReturnedEvents);
- if (!KSUCCESS(Status)) {
- goto TerminalMasterWriteEnd;
- }
- if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
- Status = STATUS_DEVICE_IO_ERROR;
- goto TerminalMasterWriteEnd;
- }
- KeAcquireQueuedLock(Terminal->InputLock);
- InputLockHeld = TRUE;
- }
- //
- // Add the character to the input buffer.
- //
- Terminal->InputBuffer[Terminal->InputBufferEnd] = Byte;
- Terminal->InputBufferEnd += 1;
- if (Terminal->InputBufferEnd == TERMINAL_INPUT_BUFFER_SIZE) {
- Terminal->InputBufferEnd = 0;
- }
- ASSERT(Terminal->InputBufferEnd != Terminal->InputBufferStart);
- InputAdded = TRUE;
- }
- //
- // Potentially echo the byte. Failure to echo is not necessarily
- // considered a failure.
- //
- if (EchoFlags != 0) {
- //
- // In raw mode, echo everything unless disallowed.
- //
- if ((LocalFlags & TERMINAL_LOCAL_CANONICAL) == 0) {
- EchoThisCharacter = FALSE;
- if ((EchoFlags & TERMINAL_LOCAL_ECHO) != 0) {
- EchoThisCharacter = TRUE;
- } else if ((Byte == '\n') &&
- ((EchoFlags & TERMINAL_LOCAL_ECHO_NEWLINE) != 0)) {
- EchoThisCharacter = TRUE;
- }
- //
- // In canonical mode, only consider echoing newlines. Everything
- // else is handled automatically.
- //
- } else {
- EchoThisCharacter = FALSE;
- if (Byte == '\n') {
- if ((EchoFlags &
- (TERMINAL_LOCAL_ECHO_NEWLINE |
- TERMINAL_LOCAL_ECHO)) != 0) {
- EchoThisCharacter = TRUE;
- }
- }
- }
- if (EchoThisCharacter != FALSE) {
- Bytes[0] = Byte;
- BytesSize = 1;
- if ((Byte < ' ') &&
- ((EchoFlags & TERMINAL_LOCAL_ECHO_CONTROL) != 0) &&
- (!RtlIsCharacterSpace(Byte)) && (Byte != '\0')) {
- Bytes[1] = Byte + '@';
- Bytes[0] = '^';
- BytesSize = 2;
- }
- IopTerminalWriteOutputBuffer(Terminal,
- Bytes,
- BytesSize,
- 1,
- TimeoutInMilliseconds);
- OutputWritten = TRUE;
- }
- }
- }
- //
- // In canonical mode, the line may need to be fixed up.
- //
- if ((DirtyRegionBegin != DirtyRegionEnd) &&
- ((EchoFlags & TERMINAL_LOCAL_ECHO) != 0)) {
- if (OutputLockHeld == FALSE) {
- KeAcquireQueuedLock(Terminal->OutputLock);
- OutputLockHeld = TRUE;
- }
- IopTerminalFixUpCanonicalLine(Terminal,
- TimeoutInMilliseconds,
- DirtyRegionBegin,
- DirtyRegionEnd,
- ScreenCursorPosition);
- OutputWritten = TRUE;
- }
- Status = STATUS_SUCCESS;
- TerminalMasterWriteEnd:
- //
- // Signal the input and/or output that there's stuff to do.
- //
- if (OutputWritten != FALSE) {
- if (OutputLockHeld == FALSE) {
- KeAcquireQueuedLock(Terminal->OutputLock);
- OutputLockHeld = TRUE;
- }
- IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, TRUE);
- }
- if (InputAdded != FALSE) {
- if (InputLockHeld == FALSE) {
- KeAcquireQueuedLock(Terminal->InputLock);
- InputLockHeld = TRUE;
- }
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, TRUE);
- }
- //
- // Release the various locks that are held.
- //
- if (InputLockHeld != FALSE) {
- KeReleaseQueuedLock(Terminal->InputLock);
- }
- if (OutputLockHeld != FALSE) {
- KeReleaseQueuedLock(Terminal->OutputLock);
- }
- IoContext->BytesCompleted = ByteIndex;
- return Status;
- }
- KSTATUS
- IopTerminalSlaveWrite (
- PFILE_OBJECT FileObject,
- PIO_CONTEXT IoContext
- )
- /*++
- Routine Description:
- This routine writes data to the terminal master (ie writes to the slaves
- standard out).
- Arguments:
- FileObject - Supplies a pointer to the slave terminal file object.
- IoContext - Supplies a pointer to the I/O context.
- Return Value:
- Status code. A failing status code does not necessarily mean no I/O made it
- in or out. Check the bytes completed value in the I/O context to find out
- how much occurred.
- --*/
- {
- BOOL AcceptingSignal;
- BOOL AnythingWritten;
- UINTN BytesThisRound;
- UINTN BytesWritten;
- UCHAR LocalBytes[64];
- BOOL LockHeld;
- PIO_OBJECT_STATE MasterIoState;
- PROCESS_GROUP_ID ProcessGroup;
- ULONG ReturnedEvents;
- SESSION_ID Session;
- BOOL SignalProcessGroup;
- PTERMINAL_SLAVE Slave;
- PIO_OBJECT_STATE SlaveIoState;
- ULONG Space;
- KSTATUS Status;
- PTERMINAL Terminal;
- ULONG TimeoutInMilliseconds;
- AnythingWritten = FALSE;
- BytesWritten = 0;
- LockHeld = FALSE;
- MasterIoState = NULL;
- SignalProcessGroup = FALSE;
- Slave = FileObject->SpecialIo;
- Terminal = Slave->Master;
- TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
- ASSERT(Slave->Header.Type == ObjectTerminalSlave);
- if (IO_IS_TERMINAL_MASTER_OPEN(Terminal) == FALSE) {
- Status = STATUS_BROKEN_PIPE;
- goto TerminalSlaveWriteEnd;
- }
- SlaveIoState = Terminal->SlaveFileObject->IoState;
- MasterIoState = Terminal->MasterFileObject->IoState;
- PsGetProcessGroup(NULL, &ProcessGroup, &Session);
- //
- // Synchronize the checks on the terminal attachment and the owning session
- // and process group with the IOCTLs that may modify them.
- //
- KeAcquireQueuedLock(Terminal->OutputLock);
- LockHeld = TRUE;
- //
- // If the writing process is not in the same process group, send the
- // process group a signal unless the writing process is ignoring or
- // blocking that signal.
- //
- if ((ProcessGroup != Terminal->ProcessGroupId) &&
- ((Terminal->Settings.LocalFlags &
- TERMINAL_LOCAL_STOP_BACKGROUND_WRITES) != 0)) {
- AcceptingSignal = PsIsThreadAcceptingSignal(
- NULL,
- SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
- //
- // If the process is accepting that signal, send it to it and tell it
- // to try again later. The exception is an orphaned process group, in
- // which case an error is returned. If the process is not accepting the
- // signal, just let the write go through.
- //
- if (AcceptingSignal != FALSE) {
- if (PsIsProcessGroupOrphaned(ProcessGroup) != FALSE) {
- Status = STATUS_DEVICE_IO_ERROR;
- } else {
- SignalProcessGroup = TRUE;
- Status = STATUS_TRY_AGAIN;
- }
- goto TerminalSlaveWriteEnd;
- }
- }
- //
- // Loop writing bytes until it's done.
- //
- Status = STATUS_SUCCESS;
- Space = IopTerminalGetOutputBufferSpace(Terminal);
- while (BytesWritten != IoContext->SizeInBytes) {
- //
- // If there's no space, release the lock and wait for space to open up.
- //
- if (Space == 0) {
- IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, TRUE);
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, FALSE);
- KeReleaseQueuedLock(Terminal->OutputLock);
- LockHeld = FALSE;
- Status = IoWaitForIoObjectState(SlaveIoState,
- POLL_EVENT_OUT,
- TRUE,
- TimeoutInMilliseconds,
- &ReturnedEvents);
- if (!KSUCCESS(Status)) {
- goto TerminalSlaveWriteEnd;
- }
- if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
- Status = STATUS_DEVICE_IO_ERROR;
- goto TerminalSlaveWriteEnd;
- }
- KeAcquireQueuedLock(Terminal->OutputLock);
- LockHeld = TRUE;
- Space = IopTerminalGetOutputBufferSpace(Terminal);
- continue;
- }
- BytesThisRound = Space;
- if (IoContext->SizeInBytes - BytesWritten < Space) {
- BytesThisRound = IoContext->SizeInBytes - BytesWritten;
- }
- //
- // Copy the data from the I/O buffer to a local bounce buffer, then
- // into the output buffer.
- //
- if (BytesThisRound > sizeof(LocalBytes)) {
- BytesThisRound = sizeof(LocalBytes);
- }
- Status = MmCopyIoBufferData(IoContext->IoBuffer,
- LocalBytes,
- BytesWritten,
- BytesThisRound,
- FALSE);
- if (!KSUCCESS(Status)) {
- break;
- }
- Status = IopTerminalWriteOutputBuffer(Terminal,
- LocalBytes,
- BytesThisRound,
- 1,
- TimeoutInMilliseconds);
- if (!KSUCCESS(Status)) {
- goto TerminalSlaveWriteEnd;
- }
- Space = IopTerminalGetOutputBufferSpace(Terminal);
- AnythingWritten = TRUE;
- BytesWritten += BytesThisRound;
- }
- //
- // Unsignal the write event if this routine just wrote the last of the
- // space.
- //
- ASSERT(LockHeld != FALSE);
- if ((AnythingWritten != FALSE) && (Space == 0)) {
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, FALSE);
- }
- TerminalSlaveWriteEnd:
- if (AnythingWritten != FALSE) {
- if (LockHeld == FALSE) {
- KeAcquireQueuedLock(Terminal->OutputLock);
- LockHeld = TRUE;
- }
- IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, TRUE);
- }
- if (LockHeld != FALSE) {
- KeReleaseQueuedLock(Terminal->OutputLock);
- }
- if (SignalProcessGroup != FALSE) {
- PsSignalProcessGroup(ProcessGroup, SIGNAL_BACKGROUND_TERMINAL_OUTPUT);
- }
- IoContext->BytesCompleted = BytesWritten;
- return Status;
- }
- KSTATUS
- IopTerminalMasterRead (
- PFILE_OBJECT FileObject,
- PIO_CONTEXT IoContext
- )
- /*++
- Routine Description:
- This routine reads data from the master side (the slave's standard out).
- Arguments:
- FileObject - Supplies a pointer to the master terminal file object.
- IoContext - Supplies a pointer to the I/O context.
- Return Value:
- Status code. A failing status code does not necessarily mean no I/O made it
- in or out. Check the bytes completed value in the I/O context to find out
- how much occurred.
- --*/
- {
- BOOL AnythingRead;
- UINTN BytesRead;
- UINTN CopySize;
- BOOL LockHeld;
- PIO_OBJECT_STATE MasterIoState;
- ULONG ReturnedEvents;
- PIO_OBJECT_STATE SlaveIoState;
- ULONG Space;
- KSTATUS Status;
- PTERMINAL Terminal;
- ULONG TimeoutInMilliseconds;
- Terminal = FileObject->SpecialIo;
- ASSERT(Terminal->Header.Type == ObjectTerminalMaster);
- ASSERT(Terminal->MasterFileObject == FileObject);
- if (Terminal->SlaveFileObject == NULL) {
- IoContext->BytesCompleted = 0;
- return STATUS_NOT_READY;
- }
- SlaveIoState = Terminal->SlaveFileObject->IoState;
- MasterIoState = FileObject->IoState;
- TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
- AnythingRead = FALSE;
- BytesRead = 0;
- KeAcquireQueuedLock(Terminal->OutputLock);
- LockHeld = TRUE;
- Space = IopTerminalGetOutputBufferSpace(Terminal);
- while (BytesRead < IoContext->SizeInBytes) {
- //
- // Wait for data to be ready.
- //
- while (Space == TERMINAL_OUTPUT_BUFFER_SIZE - 1) {
- //
- // If the caller got something already, just return immediately
- // instead of waiting for the full buffer amount.
- //
- if (AnythingRead != FALSE) {
- Status = STATUS_SUCCESS;
- goto TerminalMasterReadEnd;
- }
- IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, FALSE);
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, TRUE);
- KeReleaseQueuedLock(Terminal->OutputLock);
- LockHeld = FALSE;
- Status = IoWaitForIoObjectState(MasterIoState,
- POLL_EVENT_IN,
- TRUE,
- TimeoutInMilliseconds,
- &ReturnedEvents);
- if (!KSUCCESS(Status)) {
- goto TerminalMasterReadEnd;
- }
- if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
- Status = STATUS_DEVICE_IO_ERROR;
- goto TerminalMasterReadEnd;
- }
- KeAcquireQueuedLock(Terminal->OutputLock);
- LockHeld = TRUE;
- Space = IopTerminalGetOutputBufferSpace(Terminal);
- }
- //
- // Copy the bytes out. Don't wrap across the terminal's circular buffer.
- //
- CopySize = (TERMINAL_OUTPUT_BUFFER_SIZE - 1) - Space;
- if (CopySize > IoContext->SizeInBytes - BytesRead) {
- CopySize = IoContext->SizeInBytes - BytesRead;
- }
- if (CopySize >
- TERMINAL_OUTPUT_BUFFER_SIZE - Terminal->OutputBufferStart) {
- CopySize = TERMINAL_OUTPUT_BUFFER_SIZE -
- Terminal->OutputBufferStart;
- }
- Status = MmCopyIoBufferData(
- IoContext->IoBuffer,
- Terminal->OutputBuffer + Terminal->OutputBufferStart,
- BytesRead,
- CopySize,
- TRUE);
- if (!KSUCCESS(Status)) {
- goto TerminalMasterReadEnd;
- }
- Terminal->OutputBufferStart += CopySize;
- ASSERT(Terminal->OutputBufferStart <= TERMINAL_OUTPUT_BUFFER_SIZE);
- if (Terminal->OutputBufferStart == TERMINAL_OUTPUT_BUFFER_SIZE) {
- Terminal->OutputBufferStart = 0;
- }
- Space += CopySize;
- AnythingRead = TRUE;
- BytesRead += CopySize;
- }
- Status = STATUS_SUCCESS;
- TerminalMasterReadEnd:
- if (AnythingRead != FALSE) {
- if (LockHeld == FALSE) {
- KeAcquireQueuedLock(Terminal->OutputLock);
- LockHeld = TRUE;
- }
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, TRUE);
- Space = IopTerminalGetOutputBufferSpace(Terminal);
- if (Space == TERMINAL_OUTPUT_BUFFER_SIZE - 1) {
- IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, FALSE);
- }
- }
- if (LockHeld != FALSE) {
- KeReleaseQueuedLock(Terminal->OutputLock);
- }
- IoContext->BytesCompleted = BytesRead;
- return Status;
- }
- KSTATUS
- IopTerminalSlaveRead (
- PFILE_OBJECT FileObject,
- PIO_CONTEXT IoContext
- )
- /*++
- Routine Description:
- This routine reads data from the slave side (the slave's standard in).
- Arguments:
- FileObject - Supplies a pointer to the slave terminal file object.
- IoContext - Supplies a pointer to the I/O context.
- Return Value:
- Status code. A failing status code does not necessarily mean no I/O made it
- in or out. Check the bytes completed value in the I/O context to find out
- how much occurred.
- --*/
- {
- BOOL AcceptingSignal;
- UINTN AdvanceSize;
- BOOL AnythingRead;
- BOOL BreakForNewline;
- UINTN BytesRead;
- CHAR Character;
- PCHAR ControlCharacters;
- UINTN CopyIndex;
- UINTN CopySize;
- UCHAR FlushCount;
- UCHAR FlushTime;
- UINTN InputIndex;
- ULONG LocalFlags;
- BOOL LockHeld;
- PIO_OBJECT_STATE MasterIoState;
- PROCESS_GROUP_ID ProcessGroup;
- ULONG ReturnedEvents;
- SESSION_ID Session;
- BOOL SignalProcessGroup;
- PTERMINAL_SLAVE Slave;
- PIO_OBJECT_STATE SlaveIoState;
- ULONG Space;
- KSTATUS Status;
- PTERMINAL Terminal;
- ULONG TimeoutInMilliseconds;
- SignalProcessGroup = FALSE;
- Slave = FileObject->SpecialIo;
- ASSERT(Slave->Header.Type == ObjectTerminalSlave);
- Terminal = Slave->Master;
- ASSERT(FileObject == Terminal->SlaveFileObject);
- SlaveIoState = Terminal->SlaveFileObject->IoState;
- MasterIoState = Terminal->MasterFileObject->IoState;
- ControlCharacters = Terminal->Settings.ControlCharacters;
- TimeoutInMilliseconds = IoContext->TimeoutInMilliseconds;
- AnythingRead = FALSE;
- BytesRead = 0;
- LockHeld = FALSE;
- PsGetProcessGroup(NULL, &ProcessGroup, &Session);
- //
- // Synchronize the checks on the terminal attachment and the owning session
- // and process group with the IOCTLs that may modify them.
- //
- KeAcquireQueuedLock(Terminal->InputLock);
- LockHeld = TRUE;
- LocalFlags = Terminal->Settings.LocalFlags;
- //
- // If the reading process is not in the same process group, send the
- // process group a signal unless the reading process is ignoring or
- // blocking that signal.
- //
- if (ProcessGroup != Terminal->ProcessGroupId) {
- //
- // If it's an orphaned process, fail the I/O.
- //
- if (PsIsProcessGroupOrphaned(ProcessGroup) != FALSE) {
- Status = STATUS_DEVICE_IO_ERROR;
- goto TerminalSlaveReadEnd;
- }
- AcceptingSignal = PsIsThreadAcceptingSignal(
- NULL,
- SIGNAL_BACKGROUND_TERMINAL_INPUT);
- //
- // If the process is accepting that signal, send it to it and tell it
- // to try again later. If it's not accepting it, just let it go through.
- //
- if (AcceptingSignal != FALSE) {
- SignalProcessGroup = TRUE;
- Status = STATUS_TRY_AGAIN;
- goto TerminalSlaveReadEnd;
- }
- }
- //
- // Wait the designated amount of time, or block indefinitely.
- //
- if (TimeoutInMilliseconds == WAIT_TIME_INDEFINITE) {
- FlushTime = ControlCharacters[TerminalCharacterFlushTime];
- if (FlushTime != 0) {
- TimeoutInMilliseconds = FlushTime * 100;
- }
- }
- BreakForNewline = FALSE;
- Status = STATUS_SUCCESS;
- Space = IopTerminalGetInputBufferSpace(Terminal);
- while (BytesRead < IoContext->SizeInBytes) {
- //
- // Wait for data to be ready.
- //
- if (Space == TERMINAL_INPUT_BUFFER_SIZE - 1) {
- //
- // In non-canonical mode, observe the minimum and timeout counts.
- //
- if ((LocalFlags & TERMINAL_LOCAL_CANONICAL) == 0) {
- FlushCount = ControlCharacters[TerminalCharacterFlushCount];
- if (FlushCount != 0) {
- //
- // If there's a minimum and it's been met, stop now.
- //
- if (BytesRead >= FlushCount) {
- break;
- }
- //
- // The minimum is zero. If time is also zero, then do not block.
- //
- } else {
- if (ControlCharacters[TerminalCharacterFlushTime] == 0) {
- TimeoutInMilliseconds = 0;
- }
- }
- }
- //
- // If all open handles to the master were closed, there's never
- // going to be any more data.
- //
- if (IO_IS_TERMINAL_MASTER_OPEN(Terminal) == FALSE) {
- Status = STATUS_END_OF_FILE;
- break;
- }
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, FALSE);
- IoSetIoObjectState(MasterIoState, POLL_EVENT_OUT, TRUE);
- KeReleaseQueuedLock(Terminal->InputLock);
- LockHeld = FALSE;
- Status = IoWaitForIoObjectState(SlaveIoState,
- POLL_EVENT_IN,
- TRUE,
- TimeoutInMilliseconds,
- &ReturnedEvents);
- if (!KSUCCESS(Status)) {
- goto TerminalSlaveReadEnd;
- }
- if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
- Status = STATUS_DEVICE_IO_ERROR;
- goto TerminalSlaveReadEnd;
- }
- KeAcquireQueuedLock(Terminal->InputLock);
- LockHeld = TRUE;
- LocalFlags = Terminal->Settings.LocalFlags;
- Space = IopTerminalGetInputBufferSpace(Terminal);
- if (Space == TERMINAL_INPUT_BUFFER_SIZE - 1) {
- break;
- }
- }
- //
- // Determine how much to copy out of the terminal's input buffer.
- //
- CopySize = (TERMINAL_INPUT_BUFFER_SIZE - 1) - Space;
- if (CopySize > IoContext->SizeInBytes - BytesRead) {
- CopySize = IoContext->SizeInBytes - BytesRead;
- }
- if (CopySize >
- TERMINAL_INPUT_BUFFER_SIZE - Terminal->InputBufferStart) {
- CopySize = TERMINAL_INPUT_BUFFER_SIZE - Terminal->InputBufferStart;
- }
- //
- // If it's canonical, look for a newline and break on that.
- //
- AdvanceSize = CopySize;
- if ((LocalFlags & TERMINAL_LOCAL_CANONICAL) != 0) {
- for (CopyIndex = 0; CopyIndex < CopySize; CopyIndex += 1) {
- InputIndex = Terminal->InputBufferStart + CopyIndex;
- Character = Terminal->InputBuffer[InputIndex];
- if ((Character ==
- ControlCharacters[TerminalCharacterEndOfLine]) ||
- (Character == '\n')) {
- CopySize = CopyIndex + 1;
- AdvanceSize = CopySize;
- BreakForNewline = TRUE;
- break;
- //
- // An EOF character is treated like a "return now" character,
- // but is not reported to the user.
- //
- } else if (Character ==
- ControlCharacters[TerminalCharacterEndOfFile]) {
- CopySize = CopyIndex;
- AdvanceSize = CopySize + 1;
- BreakForNewline = TRUE;
- break;
- }
- }
- }
- Status = MmCopyIoBufferData(
- IoContext->IoBuffer,
- Terminal->InputBuffer + Terminal->InputBufferStart,
- BytesRead,
- CopySize,
- TRUE);
- if (!KSUCCESS(Status)) {
- break;
- }
- Terminal->InputBufferStart += AdvanceSize;
- ASSERT(Terminal->InputBufferStart <= TERMINAL_INPUT_BUFFER_SIZE);
- if (Terminal->InputBufferStart == TERMINAL_INPUT_BUFFER_SIZE) {
- Terminal->InputBufferStart = 0;
- }
- BytesRead += CopySize;
- Space += CopySize;
- AnythingRead = TRUE;
- //
- // If this was a newline and it's canonical mode, then let the user
- // chew on that.
- //
- if (BreakForNewline != FALSE) {
- break;
- }
- }
- ASSERT(LockHeld != FALSE);
- TerminalSlaveReadEnd:
- if (AnythingRead != FALSE) {
- if (LockHeld == FALSE) {
- KeAcquireQueuedLock(Terminal->InputLock);
- LockHeld = TRUE;
- }
- IoSetIoObjectState(MasterIoState, POLL_EVENT_OUT, TRUE);
- Space = IopTerminalGetInputBufferSpace(Terminal);
- if (Space == TERMINAL_INPUT_BUFFER_SIZE - 1) {
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_IN, FALSE);
- }
- }
- if (LockHeld != FALSE) {
- KeReleaseQueuedLock(Terminal->InputLock);
- }
- if (SignalProcessGroup != FALSE) {
- PsSignalProcessGroup(ProcessGroup, SIGNAL_BACKGROUND_TERMINAL_INPUT);
- }
- IoContext->BytesCompleted = BytesRead;
- return Status;
- }
- KSTATUS
- IopTerminalWriteOutputBuffer (
- PTERMINAL Terminal,
- PVOID Buffer,
- UINTN SizeInBytes,
- ULONG RepeatCount,
- ULONG TimeoutInMilliseconds
- )
- /*++
- Routine Description:
- This routine writes data to the terminal output buffer. It assumes the
- output lock is already held and it does not set any events. It may
- release and reacquire the output lock during the course of the routine, but
- the routine will always return with the output lock held (just like it
- started with).
- Arguments:
- Terminal - Supplies a pointer to the terminal.
- Buffer - Supplies a pointer to the buffer that contains the data to write.
- SizeInBytes - Supplies the number of bytes to write.
- RepeatCount - Supplies the number of times to write the buffer to the
- output.
- TimeoutInMilliseconds - Supplies the number of milliseconds that the I/O
- operation should be waited on before timing out. Use
- WAIT_TIME_INDEFINITE to wait forever on the I/O.
- Return Value:
- Status code.
- --*/
- {
- UCHAR Byte;
- PUCHAR ByteBuffer;
- UINTN ByteIndex;
- BOOL DidLeadingCharacter;
- BOOL LockHeld;
- ULONG Mask;
- PIO_OBJECT_STATE MasterIoState;
- ULONG OutputFlags;
- ULONG RepeatIndex;
- ULONG ReturnedEvents;
- PIO_OBJECT_STATE SlaveIoState;
- ULONG Space;
- KSTATUS Status;
- ByteBuffer = (PUCHAR)Buffer;
- DidLeadingCharacter = FALSE;
- LockHeld = TRUE;
- OutputFlags = Terminal->Settings.OutputFlags;
- MasterIoState = Terminal->MasterFileObject->IoState;
- SlaveIoState = Terminal->SlaveFileObject->IoState;
- Space = IopTerminalGetOutputBufferSpace(Terminal);
- for (RepeatIndex = 0; RepeatIndex < RepeatCount; RepeatIndex += 1) {
- for (ByteIndex = 0; ByteIndex < SizeInBytes; ByteIndex += 1) {
- //
- // Wait for space to become available.
- //
- if ((Space == 0) && (Terminal->HardwareHandle != NULL)) {
- Status = IopTerminalFlushOutputToDevice(Terminal);
- if (!KSUCCESS(Status)) {
- goto TerminalWriteOutputBufferEnd;
- }
- Space = IopTerminalGetOutputBufferSpace(Terminal);
- ASSERT(Space != 0);
- }
- while (Space == 0) {
- IoSetIoObjectState(MasterIoState, POLL_EVENT_IN, TRUE);
- IoSetIoObjectState(SlaveIoState, POLL_EVENT_OUT, FALSE);
- KeReleaseQueuedLock(Terminal->OutputLock);
- LockHeld = FALSE;
- Status = IoWaitForIoObjectState(SlaveIoState,
- POLL_EVENT_OUT,
- TRUE,
- TimeoutInMilliseconds,
- &ReturnedEvents);
- if (!KSUCCESS(Status)) {
- goto TerminalWriteOutputBufferEnd;
- }
- if ((ReturnedEvents & TERMINAL_POLL_ERRORS) != 0) {
- Status = STATUS_DEVICE_IO_ERROR;
- goto TerminalWriteOutputBufferEnd;
- }
- KeAcquireQueuedLock(Terminal->OutputLock);
- LockHeld = TRUE;
- Space = IopTerminalGetOutputBufferSpace(Terminal);
- }
- //
- // Process any output flags.
- //
- Byte = ByteBuffer[ByteIndex];
- if (Byte == '\r') {
- if ((OutputFlags & TERMINAL_OUTPUT_CR_TO_NEWLINE) != 0) {
- Byte = '\n';
- }
- } else if (Byte == '\n') {
- //
- // If \n should be translated to \r\n, then change the byte to
- // \r, and decrement the loop counter to go around again on the
- // same byte. The second time around, just output the \n.
- //
- Mask = TERMINAL_OUTPUT_POST_PROCESS |
- TERMINAL_OUTPUT_NEWLINE_TO_CRLF;
- if ((OutputFlags & Mask) == Mask) {
- if (DidLeadingCharacter == FALSE) {
- Byte = '\r';
- ByteIndex -= 1;
- DidLeadingCharacter = TRUE;
- } else {
- DidLeadingCharacter = FALSE;
- }
- }
- }
- //
- // Write the byte in.
- //
- Terminal->OutputBuffer[Terminal->OutputBufferEnd] = Byte;
- Terminal->OutputBufferEnd += 1;
- if (Terminal->OutputBufferEnd == TERMINAL_OUTPUT_BUFFER_SIZE) {
- Terminal->OutputBufferEnd = 0;
- }
- Space -= 1;
- }
- }
- if (Terminal->HardwareHandle != NULL) {
- ASSERT(LockHeld != FALSE);
- Status = IopTerminalFlushOutputToDevice(Terminal);
- if (!KSUCCESS(Status)) {
- goto TerminalWriteOutputBufferEnd;
- }
- }
- Status = STATUS_SUCCESS;
- TerminalWriteOutputBufferEnd:
- if (LockHeld == FALSE) {
- KeAcquireQueuedLock(Terminal->OutputLock);
- }
- return Status;
- }
- ULONG
- IopTerminalGetInputBufferSpace (
- PTERMINAL Terminal
- )
- /*++
- Routine Description:
- This routine returns the amount of space available in bytes in the input
- buffer of a terminal.
- Arguments:
- Terminal - Supplies a pointer to the terminal.
- Return Value:
- returns the number of bytes available in the input buffer.
- --*/
- {
- ULONG Space;
- if (Terminal->InputBufferEnd >= Terminal->InputBufferStart) {
- Space = TERMINAL_INPUT_BUFFER_SIZE - 1 -
- (Terminal->InputBufferEnd - Terminal->InputBufferStart);
- //
- // The buffer has wrapped around.
- //
- } else {
- Space = Terminal->InputBufferStart - Terminal->InputBufferEnd - 1;
- }
- return Space;
- }
- ULONG
- IopTerminalGetOutputBufferSpace (
- PTERMINAL Terminal
- )
- /*++
- Routine Description:
- This routine returns the amount of space available in bytes in the output
- buffer of a terminal.
- Arguments:
- Terminal - Supplies a pointer to the terminal.
- Return Value:
- returns the number of bytes available in the output buffer.
- --*/
- {
- ULONG Space;
- if (Terminal->OutputBufferEnd >= Terminal->OutputBufferStart) {
- Space = TERMINAL_OUTPUT_BUFFER_SIZE - 1 -
- (Terminal->OutputBufferEnd - Terminal->OutputBufferStart);
- //
- // The buffer has wrapped around.
- //
- } else {
- Space = Terminal->OutputBufferStart - Terminal->OutputBufferEnd - 1;
- }
- return Space;
- }
- KSTATUS
- IopTerminalFixUpCanonicalLine (
- PTERMINAL Terminal,
- ULONG TimeoutInMilliseconds,
- ULONG DirtyRegionBegin,
- ULONG DirtyRegionEnd,
- ULONG CurrentScreenPosition
- )
- /*++
- Routine Description:
- This routine fixes up the terminal output for canonical mode processing
- either when a block of input or a valid line is finished. It does not
- acquire any locks or set any events, it assumes that is handled by the
- caller. Specifically, the working input lock and output lock must both
- be held.
- Arguments:
- Terminal - Supplies a pointer to the terminal.
- TimeoutInMilliseconds - Supplies the amount of time to wait for output
- operations before giving up.
- DirtyRegionBegin - Supplies the first character in the dirty region, as an
- offset in characters from the beginning of the line. The beginning of
- the line may not be column zero.
- DirtyRegionEnd - Supplies the first character not in the dirty region, as
- an offset in characters from the beginning of the line.
- CurrentScreenPosition - Supplies the position of the screen's cursor as an
- offset in characters from the beginning of the line.
- Return Value:
- Status code.
- --*/
- {
- CHAR Character;
- PCHAR ControlCharacters;
- KSTATUS Status;
- ULONG ValidLineEnd;
- ULONG WorkingInputLength;
- ControlCharacters = Terminal->Settings.ControlCharacters;
- //
- // If the last character is a newline, pretend it's not there.
- //
- WorkingInputLength = Terminal->WorkingInputLength;
- if ((WorkingInputLength != 0) &&
- ((Terminal->WorkingInputBuffer[WorkingInputLength - 1] ==
- ControlCharacters[TerminalCharacterEndOfLine]) ||
- (Terminal->WorkingInputBuffer[WorkingInputLength - 1] == '\n'))) {
- WorkingInputLength -= 1;
- }
- //
- // Back up to the start of the dirty region.
- //
- ASSERT(DirtyRegionBegin <= CurrentScreenPosition);
- if (DirtyRegionBegin < CurrentScreenPosition) {
- Character = '\b';
- Status = IopTerminalWriteOutputBuffer(
- Terminal,
- &Character,
- 1,
- CurrentScreenPosition - DirtyRegionBegin,
- TimeoutInMilliseconds);
- if (!KSUCCESS(Status)) {
- goto TerminalFixUpCanonicalLineEnd;
- }
- CurrentScreenPosition = DirtyRegionBegin;
- }
- //
- // Write out the portion of the dirty region that's still a valid line.
- //
- ValidLineEnd = DirtyRegionEnd;
- if (WorkingInputLength < ValidLineEnd) {
- ValidLineEnd = WorkingInputLength;
- }
- if (ValidLineEnd > DirtyRegionBegin) {
- Status = IopTerminalWriteOutputBuffer(
- Terminal,
- Terminal->WorkingInputBuffer + DirtyRegionBegin,
- ValidLineEnd - DirtyRegionBegin,
- 1,
- TimeoutInMilliseconds);
- if (!KSUCCESS(Status)) {
- goto TerminalFixUpCanonicalLineEnd;
- }
- CurrentScreenPosition += ValidLineEnd - DirtyRegionBegin;
- }
- //
- // Write spaces to erase any additional portion that goes beyond the valid
- // line end.
- //
- if (CurrentScreenPosition < DirtyRegionEnd) {
- Character = ' ';
- Status = IopTerminalWriteOutputBuffer(
- Terminal,
- &Character,
- 1,
- DirtyRegionEnd - CurrentScreenPosition,
- TimeoutInMilliseconds);
- if (!KSUCCESS(Status)) {
- goto TerminalFixUpCanonicalLineEnd;
- }
- CurrentScreenPosition = DirtyRegionEnd;
- }
- //
- // Finally, back up to the cursor position.
- //
- if (CurrentScreenPosition > Terminal->WorkingInputCursor) {
- Character = '\b';
- Status = IopTerminalWriteOutputBuffer(
- Terminal,
- &Character,
- 1,
- CurrentScreenPosition - Terminal->WorkingInputCursor,
- TimeoutInMilliseconds);
- if (!KSUCCESS(Status)) {
- goto TerminalFixUpCanonicalLineEnd;
- }
- }
- Status = STATUS_SUCCESS;
- TerminalFixUpCanonicalLineEnd:
- return Status;
- }
- BOOL
- IopTerminalProcessEditingCharacter (
- PTERMINAL Terminal,
- CHAR Character,
- ULONG TimeoutInMilliseconds,
- PULONG DirtyRegionBegin,
- PULONG DirtyRegionEnd,
- PULONG ScreenCursorPosition,
- PBOOL OutputWritten
- )
- /*++
- Routine Description:
- This routine processes any characters that change the working buffer in a
- non-straightforward way. This routine operates in canonical mode only.
- Arguments:
- Terminal - Supplies a pointer to the terminal.
- Character - Supplies the character to process.
- TimeoutInMilliseconds - Supplies the number of milliseconds to wait if the
- output buffer is written to.
- DirtyRegionBegin - Supplies a pointer that contains the region of the line
- that needs to be redrawn. This may get expanded on output.
- DirtyRegionEnd - Supplies a pointer that contains the end of the region of
- the line that needs to be redrawn. This also may get expanded on
- output.
- ScreenCursorPosition - Supplies a pointer that contains the screen's
- current cursor position on input, and may get altered on output.
- OutputWritten - Supplies a pointer where TRUE will be returned if the
- output buffer was written to. Otherwise, this value will be left
- untouched.
- Return Value:
- TRUE if the byte was handled by this routine and should not be added to the
- working buffer.
- FALSE if the character was not handled by this routine.
- --*/
- {
- TERMINAL_COMMAND_DATA CommandData;
- PCHAR ControlCharacters;
- UINTN LastIndex;
- ULONG LocalFlags;
- UINTN MoveIndex;
- CHAR OutputString[TERMINAL_MAX_CANONICAL_OUTPUT];
- UINTN OutputStringLength;
- TERMINAL_PARSE_RESULT ParseResult;
- BOOL Result;
- ControlCharacters = Terminal->Settings.ControlCharacters;
- LocalFlags = Terminal->Settings.LocalFlags;
- ParseResult = TermProcessInput(&(Terminal->KeyData), Character);
- switch (ParseResult) {
- case TerminalParseResultNormalCharacter:
- //
- // Erase backs up one.
- //
- Result = FALSE;
- if (Character == ControlCharacters[TerminalCharacterErase]) {
- if (Terminal->WorkingInputCursor != 0) {
- Terminal->WorkingInputCursor -= 1;
- ASSERT(Terminal->WorkingInputLength != 0);
- //
- // Potentially expand the portion of the screen that will
- // need cleaning up.
- //
- if ((LocalFlags & TERMINAL_LOCAL_ECHO_ERASE) != 0) {
- if (Terminal->WorkingInputCursor < *DirtyRegionBegin) {
- *DirtyRegionBegin = Terminal->WorkingInputCursor;
- }
- if (Terminal->WorkingInputLength + 1 > *DirtyRegionEnd) {
- *DirtyRegionEnd = Terminal->WorkingInputLength + 1;
- }
- //
- // If not echoing erase, print the character that was just
- // erased to indicate what happened. This is useful for
- // line printers.
- //
- } else {
- LastIndex = Terminal->WorkingInputCursor + 1;
- OutputString[0] = Terminal->WorkingInputBuffer[LastIndex];
- IopTerminalWriteOutputBuffer(Terminal,
- OutputString,
- 1,
- 1,
- TimeoutInMilliseconds);
- }
- //
- // Move the characters after the cursor back one.
- //
- for (MoveIndex = Terminal->WorkingInputCursor;
- MoveIndex < Terminal->WorkingInputLength - 1;
- MoveIndex += 1) {
- Terminal->WorkingInputBuffer[MoveIndex] =
- Terminal->WorkingInputBuffer[MoveIndex + 1];
- }
- Terminal->WorkingInputLength -= 1;
- }
- Result = TRUE;
- //
- // Kill erases the whole line.
- //
- } else if (Character == ControlCharacters[TerminalCharacterKill]) {
- //
- // If the extended bit is set, visually erase the whole line.
- //
- if ((LocalFlags & TERMINAL_LOCAL_ECHO_KILL_EXTENDED) != 0) {
- Result = TRUE;
- *DirtyRegionBegin = 0;
- if (Terminal->WorkingInputLength > *DirtyRegionEnd) {
- *DirtyRegionEnd = Terminal->WorkingInputLength;
- }
- //
- // Otherwise if the old echo kill is set, add a newline.
- //
- } else if ((LocalFlags & TERMINAL_LOCAL_ECHO_KILL_NEWLINE) != 0) {
- Result = TRUE;
- OutputString[0] = Character;
- OutputString[1] = '\n';
- IopTerminalWriteOutputBuffer(Terminal,
- OutputString,
- 2,
- 1,
- TimeoutInMilliseconds);
- //
- // Just echo the kill character.
- //
- } else {
- Result = FALSE;
- }
- Terminal->WorkingInputCursor = 0;
- Terminal->WorkingInputLength = 0;
- Terminal->Flags &= ~(TERMINAL_FLAG_VIRGIN_LINE |
- TERMINAL_FLAG_UNEDITED_LINE);
- //
- // These other characters are simply not printed.
- //
- } else {
- if ((Character == ControlCharacters[TerminalCharacterStart]) ||
- (Character == ControlCharacters[TerminalCharacterStop])) {
- Result = TRUE;
- }
- }
- return Result;
- case TerminalParseResultPartialCommand:
- return TRUE;
- case TerminalParseResultCompleteCommand:
- break;
- default:
- ASSERT(FALSE);
- return FALSE;
- }
- //
- // Handle the complete key that just came in.
- //
- RtlZeroMemory(&CommandData, sizeof(TERMINAL_COMMAND_DATA));
- switch (Terminal->KeyData.Key) {
- case TerminalKeyPageUp:
- case TerminalKeyPageDown:
- if (Terminal->KeyData.Key == TerminalKeyPageUp) {
- CommandData.Command = TerminalCommandScrollUp;
- } else {
- CommandData.Command = TerminalCommandScrollDown;
- }
- CommandData.ParameterCount = 1;
- CommandData.Parameter[0] = TERMINAL_SCROLL_LINE_COUNT;
- Result = TermCreateOutputSequence(&CommandData,
- OutputString,
- sizeof(OutputString));
- if (Result != FALSE) {
- OutputString[sizeof(OutputString) - 1] = '\0';
- OutputStringLength = RtlStringLength(OutputString);
- IopTerminalWriteOutputBuffer(Terminal,
- OutputString,
- OutputStringLength,
- 1,
- TimeoutInMilliseconds);
- *OutputWritten = TRUE;
- }
- break;
- case TerminalKeyRight:
- if (Terminal->WorkingInputCursor != Terminal->WorkingInputLength) {
- Terminal->WorkingInputCursor += 1;
- if (Terminal->WorkingInputCursor > *DirtyRegionEnd) {
- *DirtyRegionEnd = Terminal->WorkingInputCursor;
- }
- }
- break;
- case TerminalKeyLeft:
- if (Terminal->WorkingInputCursor != 0) {
- Terminal->WorkingInputCursor -= 1;
- if (Terminal->WorkingInputCursor < *DirtyRegionBegin) {
- *DirtyRegionBegin = Terminal->WorkingInputCursor;
- }
- }
- break;
- default:
- break;
- }
- return TRUE;
- }
- KSTATUS
- IopTerminalUserBufferCopy (
- BOOL FromKernelMode,
- BOOL FromBuffer,
- PVOID UserBuffer,
- PVOID LocalBuffer,
- UINTN Size
- )
- /*++
- Routine Description:
- This routine copies to or from a user mode or kernel mode buffer.
- Arguments:
- FromKernelMode - Supplies a boolean indicating whether or not this request
- (and the buffer associated with it) originates from user mode (FALSE)
- or kernel mode (TRUE).
- FromBuffer - Supplies a boolean indicating whether to copy to the user
- buffer (FALSE) or from the user buffer (TRUE).
- UserBuffer - Supplies the user buffer pointer.
- LocalBuffer - Supplies the local kernel mode buffer.
- Size - Supplies the number of bytes to copy.
- Return Value:
- Status code.
- --*/
- {
- KSTATUS Status;
- //
- // If the caller says it's from kernel mode, it better be a kernel mode
- // address.
- //
- ASSERT((UserBuffer >= KERNEL_VA_START) || (FromKernelMode == FALSE));
- Status = STATUS_SUCCESS;
- if (FromBuffer != FALSE) {
- if (FromKernelMode != FALSE) {
- RtlCopyMemory(LocalBuffer, UserBuffer, Size);
- } else {
- Status = MmCopyFromUserMode(LocalBuffer, UserBuffer, Size);
- }
- } else {
- if (FromKernelMode != FALSE) {
- RtlCopyMemory(UserBuffer, LocalBuffer, Size);
- } else {
- Status = MmCopyToUserMode(UserBuffer, LocalBuffer, Size);
- }
- }
- return Status;
- }
- KSTATUS
- IopTerminalFlushOutputToDevice (
- PTERMINAL Terminal
- )
- /*++
- Routine Description:
- This routine writes the currently buffered output data to the hardware
- device. This routine assumes the output lock is already held.
- Arguments:
- Terminal - Supplies a pointer to the terminal whose output data should be
- flushed.
- Return Value:
- Status code.
- --*/
- {
- UINTN BytesWritten;
- IO_BUFFER IoBuffer;
- UINTN Size;
- KSTATUS Status;
- ASSERT(Terminal->HardwareHandle != NULL);
- Status = STATUS_SUCCESS;
- //
- // Loop writing portions of the output buffer.
- //
- while (TRUE) {
- //
- // If the start is greater than the end, then the buffer has wrapped
- // around and needs to be written in two steps.
- //
- if (Terminal->OutputBufferEnd < Terminal->OutputBufferStart) {
- Size = TERMINAL_OUTPUT_BUFFER_SIZE - Terminal->OutputBufferStart;
- Status = MmInitializeIoBuffer(
- &IoBuffer,
- Terminal->OutputBuffer + Terminal->OutputBufferStart,
- INVALID_PHYSICAL_ADDRESS,
- Size,
- IO_BUFFER_FLAG_KERNEL_MODE_DATA);
- if (!KSUCCESS(Status)) {
- return Status;
- }
- Status = IoWrite(Terminal->HardwareHandle,
- &IoBuffer,
- Size,
- 0,
- WAIT_TIME_INDEFINITE,
- &BytesWritten);
- if (!KSUCCESS(Status)) {
- return Status;
- }
- Terminal->OutputBufferStart += BytesWritten;
- if (Terminal->OutputBufferStart == TERMINAL_OUTPUT_BUFFER_SIZE) {
- Terminal->OutputBufferStart = 0;
- }
- continue;
- }
- //
- // If the buffer is empty, stop.
- //
- if (Terminal->OutputBufferStart == Terminal->OutputBufferEnd) {
- break;
- }
- Size = Terminal->OutputBufferEnd - Terminal->OutputBufferStart;
- Status = MmInitializeIoBuffer(
- &IoBuffer,
- Terminal->OutputBuffer + Terminal->OutputBufferStart,
- INVALID_PHYSICAL_ADDRESS,
- Size,
- IO_BUFFER_FLAG_KERNEL_MODE_DATA);
- if (!KSUCCESS(Status)) {
- return Status;
- }
- Status = IoWrite(Terminal->HardwareHandle,
- &IoBuffer,
- Size,
- 0,
- WAIT_TIME_INDEFINITE,
- &BytesWritten);
- if (!KSUCCESS(Status)) {
- return Status;
- }
- Terminal->OutputBufferStart += BytesWritten;
- ASSERT(Terminal->OutputBufferStart != TERMINAL_OUTPUT_BUFFER_SIZE);
- }
- return Status;
- }
- VOID
- IopTerminalDisassociate (
- PTERMINAL Terminal
- )
- /*++
- Routine Description:
- This routine clears the controlling terminal from every process in the
- terminal's session. This routine assumes the terminal list lock and
- terminal locks are already held.
- Arguments:
- Terminal - Supplies a pointer to the controlling terminal.
- Return Value:
- None.
- --*/
- {
- SESSION_ID SessionId;
- SessionId = Terminal->SessionId;
- if (SessionId != TERMINAL_INVALID_SESSION) {
- PsIterateProcess(ProcessIdSession,
- SessionId,
- IopTerminalDisassociateIterator,
- NULL);
- }
- Terminal->SessionId = TERMINAL_INVALID_SESSION;
- Terminal->ProcessGroupId = TERMINAL_INVALID_PROCESS_GROUP;
- return;
- }
- BOOL
- IopTerminalDisassociateIterator (
- PVOID Context,
- PKPROCESS Process
- )
- /*++
- Routine Description:
- This routine describes the prototype for the process list iterator. This
- routine is called with the process list lock held.
- Arguments:
- Context - Supplies a pointer's worth of context passed into the iterate
- routine.
- Process - Supplies the process to examine.
- Return Value:
- FALSE always to indicate continuing to iterate.
- --*/
- {
- Process->ControllingTerminal = NULL;
- return FALSE;
- }
|