ps.c 76 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035
  1. /*++
  2. Copyright (c) 2013 Minoca Corp. All Rights Reserved
  3. Module Name:
  4. ps.c
  5. Abstract:
  6. This module implements the ps (process status) utility.
  7. Author:
  8. Chris Stevens 12-Aug-2014
  9. Environment:
  10. POSIX
  11. --*/
  12. //
  13. // ------------------------------------------------------------------- Includes
  14. //
  15. #include <minoca/lib/types.h>
  16. #include <assert.h>
  17. #include <errno.h>
  18. #include <getopt.h>
  19. #include <libgen.h>
  20. #include <stdlib.h>
  21. #include <string.h>
  22. #include <unistd.h>
  23. #include "swlib.h"
  24. //
  25. // ---------------------------------------------------------------- Definitions
  26. //
  27. #define PS_VERSION_MAJOR 1
  28. #define PS_VERSION_MINOR 0
  29. #define PS_USAGE \
  30. "usage: ps [-aA] [-defl] [-G grouplist] [-p proclist] [-t termlist] \n" \
  31. " [-U userlist] [-g grouplist] [-u userlist] [-o format]\n\n" \
  32. "The ps utility writes process status to standard out. Options are:\n" \
  33. " -a --all-terminals -- Write status for all processes associated with\n" \
  34. " terminals.\n" \
  35. " -A --all -- Write status for all processes.\n" \
  36. " -d --all-no-leaders -- Write status for all processes except session\n" \
  37. " leaders.\n" \
  38. " -e --all -- Write status for all processes. (Equivalent to -A).\n" \
  39. " -f --full -- Write the full status format.\n" \
  40. " -g --group=grouplist -- Write status for all processes whose session\n" \
  41. " leaders are in the given group list.\n" \
  42. " -G --Group=grouplist -- Write status for all processes whose real\n" \
  43. " group ID's are in the group list.\n" \
  44. " -l --long -- Write the long status format.\n" \
  45. " -o --format=format -- Override the default format with a " \
  46. "comma-separated\n" \
  47. " list of process status data types.\n" \
  48. " -p --pid=pidlist -- Write status for the processes whose process IDs\n" \
  49. " are in the given process list.\n" \
  50. " -t --tty=termlist -- Write status for the processes whose terminals\n" \
  51. " are in the given terminal list.\n" \
  52. " -u --user=userlist -- Write status for the processes whose user ID\n" \
  53. " number or login name are in the given user list.\n" \
  54. " -U --User=userlist -- Write status for the processes whose real user\n" \
  55. " ID or login name are in the given user list.\n" \
  56. " --help -- Show this help text and exit.\n" \
  57. " --version -- Print the application version information and exit.\n"
  58. #define PS_OPTIONS_STRING "aAdefg:G:lo:p:t:u:U:hV"
  59. //
  60. // Define ps options.
  61. //
  62. //
  63. // Set this option to report information for all processes associated with
  64. // terminals.
  65. //
  66. #define PS_OPTION_REPORT_ALL_TERMINAL_PROCESSES 0x00000001
  67. //
  68. // Set this option to report information for all processes.
  69. //
  70. #define PS_OPTION_REPORT_ALL_PROCESSES 0x00000002
  71. //
  72. // Set this option to report information for all processes, except session
  73. // leaders.
  74. //
  75. #define PS_OPTION_REPORT_ALL_PROCESSES_NO_LEADERS 0x00000004
  76. //
  77. // Set this option to generate a full report.
  78. //
  79. #define PS_OPTION_FULL_REPORT 0x00000008
  80. //
  81. // Set this option to report information for all processes whose session
  82. // leaders are in the session leader list.
  83. //
  84. #define PS_OPTION_SESSION_LEADERS_LIST 0x00000010
  85. //
  86. // Set this option to report information for all processes whose real group IDs
  87. // are in the real group ID list.
  88. //
  89. #define PS_OPTION_REAL_GROUP_ID_LIST 0x00000020
  90. //
  91. // Set this option to generate a long repot.
  92. //
  93. #define PS_OPTION_LONG_REPORT 0x00000040
  94. //
  95. // Set this option to generate a long repot.
  96. //
  97. #define PS_OPTION_CUSTOM_FORMAT 0x00000080
  98. //
  99. // Set this option to report information for all the processes whose process
  100. // IDs are in the process ID list.
  101. //
  102. #define PS_OPTION_PROCESS_ID_LIST 0x00000100
  103. //
  104. // Set this option to report information for all processes whose terminals are
  105. // in the terminal list.
  106. //
  107. #define PS_OPTION_TERMINAL_LIST 0x00000200
  108. //
  109. // Set this option to report information for all processes whose user ID or
  110. // login name appear in the user list.
  111. //
  112. #define PS_OPTION_USER_LIST 0x00000400
  113. //
  114. // Set this option to report information for all processes whose real user ID
  115. // or login name appear in the real user list.
  116. //
  117. #define PS_OPTION_REAL_USER_LIST 0x00000800
  118. //
  119. // Define a mask of all report format types.
  120. //
  121. #define PS_OPTION_REPORT_MASK (PS_OPTION_FULL_REPORT | PS_OPTION_LONG_REPORT)
  122. //
  123. // Define a mask of all process filter types.
  124. //
  125. #define PS_OPTION_FILTER_MASK \
  126. (PS_OPTION_REPORT_ALL_TERMINAL_PROCESSES | \
  127. PS_OPTION_REPORT_ALL_PROCESSES | \
  128. PS_OPTION_REPORT_ALL_PROCESSES_NO_LEADERS | \
  129. PS_OPTION_SESSION_LEADERS_LIST | \
  130. PS_OPTION_REAL_GROUP_ID_LIST | \
  131. PS_OPTION_PROCESS_ID_LIST | \
  132. PS_OPTION_TERMINAL_LIST | \
  133. PS_OPTION_USER_LIST | \
  134. PS_OPTION_REAL_USER_LIST)
  135. //
  136. // Define the number of format options in the default set.
  137. //
  138. #define PS_DEFAULT_REPORT_COUNT 15
  139. //
  140. // Define the flags for the default report.
  141. //
  142. #define PS_DEFAULT_REPORT_FLAG_BASIC 0x01
  143. #define PS_DEFAULT_REPORT_FLAG_LONG 0x02
  144. #define PS_DEFAULT_REPORT_FLAG_FULL 0x04
  145. #define PS_DEFAULT_REPORT_FLAG_ALL 0x07
  146. //
  147. // Define the fudge factor to give the process list ID query a better chance of
  148. // success.
  149. //
  150. #define PS_PROCESS_LIST_FUDGE_FACTOR 2
  151. //
  152. // Define the initial number of processes to
  153. //
  154. #define PS_PROCESS_LIST_INITIAL_COUNT 10
  155. //
  156. // Define the number or retries allowed to gather the list of process IDs.
  157. //
  158. #define PS_GET_PROCESS_LIST_RETRY_COUNT 10
  159. //
  160. // Define the character that represents data that could not be retrieved for
  161. // the process.
  162. //
  163. #define PS_MISSING_DATA_CHARACTER '-'
  164. //
  165. // Define time formats for the CPU time data.
  166. //
  167. #define PS_CPU_TIME_DAYS_FORMAT "%d-%02d:%02d:%02d"
  168. #define PS_CPU_TIME_DEFAULT_FORMAT "%02d:%02d:%02d"
  169. //
  170. // Define time formats for the elapsed time data.
  171. //
  172. #define PS_ELAPSED_TIME_DAYS_FORMAT "%d-%02d:%02d:%02d"
  173. #define PS_ELAPSED_TIME_HOURS_FORMAT "%02d:%02d:%02d"
  174. #define PS_ELAPSED_TIME_DEFAULT_FORMAT "%02d:%02d"
  175. //
  176. // Define basic date values.
  177. //
  178. #define PS_MONTHS_PER_YEAR 12
  179. #define PS_SECONDS_PER_DAY (PS_SECONDS_PER_HOUR * PS_HOURS_PER_DAY)
  180. #define PS_SECONDS_PER_HOUR (PS_SECONDS_PER_MINUTE * PS_MINUTES_PER_HOUR)
  181. #define PS_SECONDS_PER_MINUTE 60
  182. #define PS_MINUTES_PER_HOUR 60
  183. #define PS_HOURS_PER_DAY 24
  184. //
  185. // Define the invalid terminal ID.
  186. //
  187. #define INVALID_TERMINAL_ID (int)-1
  188. //
  189. // ------------------------------------------------------ Data Type Definitions
  190. //
  191. typedef enum _PS_DATA_TYPE {
  192. PsDataFlags,
  193. PsDataState,
  194. PsDataUserIdentifier,
  195. PsDataUserIdentifierText,
  196. PsDataRealUserIdentifier,
  197. PsDataEffectiveUserIdentifier,
  198. PsDataProcessIdentifier,
  199. PsDataParentProcessIdentifier,
  200. PsDataProcessGroupIdentifier,
  201. PsDataRealGroupIdentifier,
  202. PsDataEffectiveGroupIdentifier,
  203. PsDataSchedulingTime,
  204. PsDataPriority,
  205. PsDataNiceValue,
  206. PsDataAddress,
  207. PsDataBlockSize,
  208. PsDataVirtualSize,
  209. PsDataWaitEvent,
  210. PsDataStartTime,
  211. PsDataTerminal,
  212. PsDataElapsedTime,
  213. PsDataCpuTime,
  214. PsDataCmdName,
  215. PsDataCmdArguments,
  216. PsDataCommandName,
  217. PsDataCommandArguments,
  218. PsDataCpuPercentage,
  219. PsDataTypeMax,
  220. PsDataTypeInvalid
  221. } PS_DATA_TYPE, *PPS_DATA_TYPE;
  222. /*++
  223. Structure Description:
  224. This structure defines a data column that can be included in one of the
  225. three default reports (basic, full, and long).
  226. Members:
  227. Type - Stores the type of data to display in the column.
  228. FullType - Stores the type of data to display in the column if a full
  229. report is requested.
  230. Flags - Stores a bitmask of report flags. See PS_DEFAULT_REPORT_FLAG_* for
  231. definitions.
  232. --*/
  233. typedef struct _PS_DEFAULT_REPORT {
  234. PS_DATA_TYPE Type;
  235. PS_DATA_TYPE FullType;
  236. UCHAR Flags;
  237. } PS_DEFAULT_REPORT, *PPS_DEFAULT_REPORT;
  238. /*++
  239. Structure Description:
  240. This structure defines a mapping between a custom format command line
  241. string and the data type to display in the column.
  242. Members:
  243. Format - Stores the string name of the data type to display in the column.
  244. Type - Stores the data type represented by the string.
  245. --*/
  246. typedef struct _PS_CUSTOM_FORMAT_MAP_ENTRY {
  247. PSTR Format;
  248. PS_DATA_TYPE Type;
  249. } PS_CUSTOM_FORMAT_MAP_ENTRY, *PPS_CUSTOM_FORMAT_MAP_ENTRY;
  250. /*++
  251. Structure Description:
  252. This structure defines column display information.
  253. Members:
  254. Header - Stores a string containing the header of the column.
  255. Width - Stores the width of the column, in characters.
  256. RightJustified - Stores whether or not the header and data value should
  257. be right justified in the column.
  258. --*/
  259. typedef struct _PS_COLUMN {
  260. PSTR Header;
  261. ULONG Width;
  262. BOOL RightJustified;
  263. } PS_COLUMN, *PPS_COLUMN;
  264. /*++
  265. Structure Description:
  266. This structure defines a custom display format entry.
  267. Members:
  268. ListEntry - Stores pointers to the next and previous custom format entries.
  269. Type - Stores the data type to display for the custom format column.
  270. HeaderOverrideValid - Stores a boolean indicating whether or not the header
  271. override string is valid.
  272. HeaderOverrideWidth - Stores the width of the column, in characters, when
  273. the header override is valid.
  274. HeaderOverride - Stores the header override string.
  275. --*/
  276. typedef struct _PS_CUSTOM_FORMAT_ENTRY {
  277. LIST_ENTRY ListEntry;
  278. PS_DATA_TYPE Type;
  279. BOOL HeaderOverrideValid;
  280. ULONG HeaderOverrideWidth;
  281. PSTR HeaderOverride;
  282. } PS_CUSTOM_FORMAT_ENTRY, *PPS_CUSTOM_FORMAT_ENTRY;
  283. /*++
  284. Structure Description:
  285. This structure defines an entry into a process filter list. This is used to
  286. determine which processes should be included in the status report.
  287. Members:
  288. ListEntry - Stores pointers to the next and previous filter entries.
  289. NumericId - Stores the numeric ID to filter on (e.g. process ID, session ID,
  290. group ID, etc.).
  291. TextId - Stores the string ID to filter on (e.g. terminal name, group name,
  292. etc.).
  293. --*/
  294. typedef struct _PS_FILTER_ENTRY {
  295. LIST_ENTRY ListEntry;
  296. ULONG NumericId;
  297. PSTR TextId;
  298. } PS_FILTER_ENTRY, *PPS_FILTER_ENTRY;
  299. /*++
  300. Structure Description:
  301. This structure defines the application context for the PS utility.
  302. Members:
  303. CustomFormatList - Stores the head of the list of custom column formats.
  304. SessionLeaderList - Stores the head of the list of session leader IDs to
  305. filter on.
  306. RealGroupIdList - Stores the head of the list of real group IDs to filter
  307. on.
  308. TerminalList - Stores the head of the list of terminal IDs to filter on.
  309. UserIdList - Stores the head of the list of user IDs to filter on.
  310. RealUserIdList - Stores the head of the list of real user IDs to filter on.
  311. ProcessIdList - Stores an array of process IDs to filter on.
  312. ProcessIdCount - Stores the number of process IDs in the array.
  313. DisplayHeaderLine - Stores a boolean indicating whether or not to display
  314. the line of headers.
  315. --*/
  316. typedef struct _PS_CONTEXT {
  317. LIST_ENTRY CustomFormatList;
  318. LIST_ENTRY SessionLeaderList;
  319. LIST_ENTRY RealGroupIdList;
  320. LIST_ENTRY TerminalList;
  321. LIST_ENTRY UserIdList;
  322. LIST_ENTRY RealUserIdList;
  323. pid_t *ProcessIdList;
  324. UINTN ProcessIdCount;
  325. BOOL DisplayHeaderLine;
  326. } PS_CONTEXT, *PPS_CONTEXT;
  327. //
  328. // ----------------------------------------------- Internal Function Prototypes
  329. //
  330. VOID
  331. PspInitializeContext (
  332. PPS_CONTEXT Context
  333. );
  334. VOID
  335. PspDestroyContext (
  336. PPS_CONTEXT Context
  337. );
  338. VOID
  339. PspPrintHeaders (
  340. PPS_CONTEXT Context,
  341. ULONG Options
  342. );
  343. VOID
  344. PspPrintProcessInformation (
  345. PPS_CONTEXT Context,
  346. ULONG Options,
  347. PSWISS_PROCESS_INFORMATION Information
  348. );
  349. VOID
  350. PspPrintDataType (
  351. PS_DATA_TYPE Type,
  352. PSWISS_PROCESS_INFORMATION Information
  353. );
  354. VOID
  355. PspFilterProcessInformationList (
  356. PPS_CONTEXT Context,
  357. ULONG Options,
  358. PSWISS_PROCESS_INFORMATION *ProcessInformationList,
  359. UINTN ProcessCount
  360. );
  361. INT
  362. PspParseFormatList (
  363. PPS_CONTEXT Context,
  364. PSTR String
  365. );
  366. INT
  367. PspParseFilterList (
  368. PLIST_ENTRY ListHead,
  369. PSTR String
  370. );
  371. INT
  372. PspParseProcessList (
  373. PPS_CONTEXT Context,
  374. PSTR String
  375. );
  376. PSWISS_PROCESS_INFORMATION *
  377. PspGetProcessInformationList (
  378. pid_t *ProcessIdList,
  379. UINTN ProcessCount
  380. );
  381. VOID
  382. PspDestroyProcessInformationList (
  383. PSWISS_PROCESS_INFORMATION *ProcessInformationList,
  384. UINTN ProcessCount
  385. );
  386. VOID
  387. PspRemoveDuplicateProcessIds (
  388. pid_t *ProcessIdList,
  389. PUINTN ProcessIdCount
  390. );
  391. INT
  392. PspCompareProcessIds (
  393. const VOID *First,
  394. const VOID *Second
  395. );
  396. //
  397. // -------------------------------------------------------------------- Globals
  398. //
  399. struct option PsLongOptions[] = {
  400. {"all-terminals", no_argument, 0, 'a'},
  401. {"all", no_argument, 0, 'A'},
  402. {"all-no-leaders", no_argument, 0, 'd'},
  403. {"full", no_argument, 0, 'f'},
  404. {"group", required_argument, 0, 'g'},
  405. {"Group", required_argument, 0, 'G'},
  406. {"long", no_argument, 0, 'l'},
  407. {"format", required_argument, 0, 'o'},
  408. {"pid", required_argument, 0, 'p'},
  409. {"tty", required_argument, 0, 't'},
  410. {"user", required_argument, 0, 'u'},
  411. {"User", required_argument, 0, 'U'},
  412. {"help", no_argument, 0, 'h'},
  413. {"version", no_argument, 0, 'V'},
  414. {NULL, 0, 0, 0},
  415. };
  416. //
  417. // Store the set of column information for each data type.
  418. //
  419. PS_COLUMN PsColumnInformation[PsDataTypeMax] = {
  420. {"F", 1, TRUE},
  421. {"S", 1, FALSE},
  422. {"UID", 5, TRUE},
  423. {"UID", 7, FALSE},
  424. {"RUSER", 7, FALSE},
  425. {"USER", 7, FALSE},
  426. {"PID", 5, TRUE},
  427. {"PPID", 5, TRUE},
  428. {"PGID", 5, TRUE},
  429. {"RGROUP", 7, FALSE},
  430. {"GROUP", 7, FALSE},
  431. {"C", 2, TRUE},
  432. {"PRI", 3, TRUE},
  433. {"NI", 3, TRUE},
  434. {"ADDR", 4, TRUE},
  435. {"SZ", 5, TRUE},
  436. {"VSZ", 6, TRUE},
  437. {"WCHAN", 6, FALSE},
  438. {"STIME", 5, TRUE},
  439. {"TTY", 8, FALSE},
  440. {"ELAPSED", 12, TRUE},
  441. {"TIME", 12, TRUE},
  442. {"CMD", 15, FALSE},
  443. {"CMD", 27, FALSE},
  444. {"COMMAND", 15, FALSE},
  445. {"COMMAND", 27, FALSE},
  446. {"CPU%", 4, TRUE},
  447. };
  448. //
  449. // Store the set of supported custom column formats associated the command line
  450. // option with a data type.
  451. //
  452. PS_CUSTOM_FORMAT_MAP_ENTRY PsCustomFormatMap[] = {
  453. {"addr", PsDataAddress},
  454. {"args", PsDataCommandArguments},
  455. {"c", PsDataSchedulingTime},
  456. {"cmd", PsDataCmdArguments},
  457. {"comm", PsDataCommandName},
  458. {"etime", PsDataElapsedTime},
  459. {"f", PsDataFlags},
  460. {"flag", PsDataFlags},
  461. {"flags", PsDataFlags},
  462. {"group", PsDataEffectiveGroupIdentifier},
  463. {"nice", PsDataNiceValue},
  464. {"pcpu", PsDataCpuPercentage},
  465. {"pgid", PsDataProcessGroupIdentifier},
  466. {"pid", PsDataProcessIdentifier},
  467. {"ppid", PsDataParentProcessIdentifier},
  468. {"pri", PsDataPriority},
  469. {"rgroup", PsDataRealGroupIdentifier},
  470. {"ruser", PsDataRealUserIdentifier},
  471. {"s", PsDataState},
  472. {"state", PsDataState},
  473. {"stime", PsDataStartTime},
  474. {"sz", PsDataBlockSize},
  475. {"time", PsDataCpuTime},
  476. {"tty", PsDataTerminal},
  477. {"uid", PsDataUserIdentifier},
  478. {"user", PsDataEffectiveUserIdentifier},
  479. {"vsz", PsDataVirtualSize},
  480. {"wchan", PsDataWaitEvent},
  481. {NULL, PsDataTypeMax},
  482. };
  483. //
  484. // Store the default reports information.
  485. //
  486. PS_DEFAULT_REPORT PsDefaultReports[PS_DEFAULT_REPORT_COUNT] = {
  487. {PsDataFlags, PsDataTypeInvalid, PS_DEFAULT_REPORT_FLAG_LONG},
  488. {PsDataState, PsDataTypeInvalid, PS_DEFAULT_REPORT_FLAG_LONG},
  489. {PsDataUserIdentifier,
  490. PsDataUserIdentifierText,
  491. (PS_DEFAULT_REPORT_FLAG_LONG | PS_DEFAULT_REPORT_FLAG_FULL)},
  492. {PsDataProcessIdentifier,
  493. PsDataProcessIdentifier,
  494. PS_DEFAULT_REPORT_FLAG_ALL},
  495. {PsDataParentProcessIdentifier,
  496. PsDataParentProcessIdentifier,
  497. (PS_DEFAULT_REPORT_FLAG_LONG | PS_DEFAULT_REPORT_FLAG_FULL)},
  498. {PsDataSchedulingTime,
  499. PsDataSchedulingTime,
  500. (PS_DEFAULT_REPORT_FLAG_LONG | PS_DEFAULT_REPORT_FLAG_FULL)},
  501. {PsDataPriority, PsDataTypeInvalid, PS_DEFAULT_REPORT_FLAG_LONG},
  502. {PsDataNiceValue, PsDataTypeInvalid, PS_DEFAULT_REPORT_FLAG_LONG},
  503. {PsDataAddress, PsDataTypeInvalid, PS_DEFAULT_REPORT_FLAG_LONG},
  504. {PsDataBlockSize, PsDataTypeInvalid, PS_DEFAULT_REPORT_FLAG_LONG},
  505. {PsDataWaitEvent, PsDataTypeInvalid, PS_DEFAULT_REPORT_FLAG_LONG},
  506. {PsDataStartTime, PsDataStartTime, PS_DEFAULT_REPORT_FLAG_FULL},
  507. {PsDataTerminal, PsDataTerminal, PS_DEFAULT_REPORT_FLAG_ALL},
  508. {PsDataCpuTime, PsDataCpuTime, PS_DEFAULT_REPORT_FLAG_ALL},
  509. {PsDataCmdName, PsDataCmdArguments, PS_DEFAULT_REPORT_FLAG_ALL},
  510. };
  511. //
  512. // Store the month abbreviations.
  513. //
  514. PSTR PsMonths[PS_MONTHS_PER_YEAR] = {
  515. "Jan",
  516. "Feb",
  517. "Mar",
  518. "Apr",
  519. "May",
  520. "Jun",
  521. "Jul",
  522. "Aug",
  523. "Sep",
  524. "Oct",
  525. "Nov",
  526. "Dec"
  527. };
  528. //
  529. // Store the display output for different process states.
  530. //
  531. PSTR PsProcessStateStrings[SwissProcessStateMax] = {
  532. "R",
  533. "D",
  534. "S",
  535. "T",
  536. "X",
  537. "Z",
  538. "?"
  539. };
  540. //
  541. // ------------------------------------------------------------------ Functions
  542. //
  543. INT
  544. PsMain (
  545. INT ArgumentCount,
  546. CHAR **Arguments
  547. )
  548. /*++
  549. Routine Description:
  550. This routine is the main entry point for the ps utility.
  551. Arguments:
  552. ArgumentCount - Supplies the number of command line arguments the program
  553. was invoked with.
  554. Arguments - Supplies a tokenized array of command line arguments.
  555. Return Value:
  556. Returns an integer exit code. 0 for success, non-zero otherwise.
  557. --*/
  558. {
  559. PSTR Argument;
  560. PS_CONTEXT Context;
  561. UINTN Index;
  562. INT Option;
  563. ULONG Options;
  564. UINTN ProcessCount;
  565. pid_t *ProcessIdList;
  566. size_t ProcessIdListSize;
  567. PSWISS_PROCESS_INFORMATION *ProcessInformationList;
  568. UINTN Retry;
  569. int Status;
  570. PspInitializeContext(&Context);
  571. Options = 0;
  572. ProcessIdList = NULL;
  573. ProcessIdListSize = 0;
  574. ProcessInformationList = NULL;
  575. //
  576. // Process the control arguments.
  577. //
  578. while (TRUE) {
  579. Option = getopt_long(ArgumentCount,
  580. Arguments,
  581. PS_OPTIONS_STRING,
  582. PsLongOptions,
  583. NULL);
  584. if (Option == -1) {
  585. break;
  586. }
  587. if ((Option == '?') || (Option == ':')) {
  588. Status = 1;
  589. goto MainEnd;
  590. }
  591. switch (Option) {
  592. case 'a':
  593. Options |= PS_OPTION_REPORT_ALL_TERMINAL_PROCESSES;
  594. break;
  595. case 'A':
  596. case 'e':
  597. Options |= PS_OPTION_REPORT_ALL_PROCESSES;
  598. break;
  599. case 'd':
  600. Options |= PS_OPTION_REPORT_ALL_PROCESSES_NO_LEADERS;
  601. break;
  602. case 'f':
  603. Options |= PS_OPTION_FULL_REPORT;
  604. break;
  605. case 'g':
  606. Options |= PS_OPTION_SESSION_LEADERS_LIST;
  607. Argument = optarg;
  608. assert(Arguments != NULL);
  609. Status = PspParseFilterList(&(Context.SessionLeaderList), Argument);
  610. if (Status != 0) {
  611. goto MainEnd;
  612. }
  613. break;
  614. case 'G':
  615. Options |= PS_OPTION_REAL_GROUP_ID_LIST;
  616. Argument = optarg;
  617. assert(Arguments != NULL);
  618. Status = PspParseFilterList(&(Context.RealGroupIdList), Argument);
  619. if (Status != 0) {
  620. goto MainEnd;
  621. }
  622. break;
  623. case 'l':
  624. Options |= PS_OPTION_LONG_REPORT;
  625. break;
  626. case 'o':
  627. Options |= PS_OPTION_CUSTOM_FORMAT;
  628. Argument = optarg;
  629. assert(Argument != NULL);
  630. Status = PspParseFormatList(&Context, Argument);
  631. if (Status != 0) {
  632. goto MainEnd;
  633. }
  634. break;
  635. case 'p':
  636. Options |= PS_OPTION_PROCESS_ID_LIST;
  637. Argument = optarg;
  638. assert(Argument != NULL);
  639. Status = PspParseProcessList(&Context, Argument);
  640. if (Status != 0) {
  641. goto MainEnd;
  642. }
  643. assert(Context.ProcessIdList != NULL);
  644. break;
  645. case 't':
  646. Options |= PS_OPTION_TERMINAL_LIST;
  647. Argument = optarg;
  648. assert(Arguments != NULL);
  649. Status = PspParseFilterList(&(Context.TerminalList), Argument);
  650. if (Status != 0) {
  651. goto MainEnd;
  652. }
  653. break;
  654. case 'u':
  655. Options |= PS_OPTION_USER_LIST;
  656. Argument = optarg;
  657. assert(Arguments != NULL);
  658. Status = PspParseFilterList(&(Context.UserIdList), Argument);
  659. if (Status != 0) {
  660. goto MainEnd;
  661. }
  662. break;
  663. case 'U':
  664. Options |= PS_OPTION_REAL_USER_LIST;
  665. Argument = optarg;
  666. assert(Arguments != NULL);
  667. Status = PspParseFilterList(&(Context.RealUserIdList), Argument);
  668. if (Status != 0) {
  669. goto MainEnd;
  670. }
  671. break;
  672. case 'V':
  673. SwPrintVersion(PS_VERSION_MAJOR, PS_VERSION_MINOR);
  674. return 1;
  675. case 'h':
  676. printf(PS_USAGE);
  677. return 1;
  678. default:
  679. assert(FALSE);
  680. Status = 1;
  681. goto MainEnd;
  682. }
  683. }
  684. //
  685. // If a custom format is supplied, then neither the 'long' or 'full'
  686. // arguments should be supplied.
  687. //
  688. if (((Options & PS_OPTION_CUSTOM_FORMAT) != 0) &&
  689. ((Options & (PS_OPTION_LONG_REPORT | PS_OPTION_FULL_REPORT)) != 0)) {
  690. SwPrintError(EINVAL, NULL, "Conflicting format options");
  691. printf(PS_USAGE);
  692. Status = 1;
  693. goto MainEnd;
  694. }
  695. //
  696. // In most cases, the entire list of process IDs is needs to be collected
  697. // and then filtered. The exception is if only a process list was specified
  698. // on the command line.
  699. //
  700. if ((Options & PS_OPTION_FILTER_MASK) != PS_OPTION_PROCESS_ID_LIST) {
  701. Retry = 0;
  702. ProcessIdListSize = PS_PROCESS_LIST_INITIAL_COUNT * sizeof(pid_t);
  703. do {
  704. Retry += 1;
  705. if (ProcessIdList != NULL) {
  706. free(ProcessIdList);
  707. }
  708. ProcessIdList = malloc(ProcessIdListSize);
  709. if (ProcessIdList == NULL) {
  710. Status = 1;
  711. goto MainEnd;
  712. }
  713. Status = SwGetProcessIdList(ProcessIdList, &ProcessIdListSize);
  714. if (Status == 0) {
  715. break;
  716. }
  717. ProcessIdListSize *= PS_PROCESS_LIST_FUDGE_FACTOR;
  718. } while (Retry <= PS_GET_PROCESS_LIST_RETRY_COUNT);
  719. if (Status != 0) {
  720. goto MainEnd;
  721. }
  722. ProcessCount = ProcessIdListSize / sizeof(pid_t);
  723. //
  724. // Sort the process ID list. The system shouldn't be returning
  725. // duplicates, so skip that step.
  726. //
  727. qsort(ProcessIdList,
  728. ProcessCount,
  729. sizeof(pid_t),
  730. PspCompareProcessIds);
  731. //
  732. // Get the list of process information for the process IDs.
  733. //
  734. ProcessInformationList = PspGetProcessInformationList(ProcessIdList,
  735. ProcessCount);
  736. if (ProcessInformationList == NULL) {
  737. Status = -1;
  738. goto MainEnd;
  739. }
  740. //
  741. // With a list of all the process information, filter it based on the
  742. // command line options.
  743. //
  744. PspFilterProcessInformationList(&Context,
  745. Options,
  746. ProcessInformationList,
  747. ProcessCount);
  748. } else {
  749. ProcessIdList = Context.ProcessIdList;
  750. ProcessCount = Context.ProcessIdCount;
  751. //
  752. // Remove duplicates from the list. This will sort the list as well.
  753. //
  754. PspRemoveDuplicateProcessIds(ProcessIdList, &ProcessCount);
  755. //
  756. // Get the list of process information for the process IDs.
  757. //
  758. ProcessInformationList = PspGetProcessInformationList(ProcessIdList,
  759. ProcessCount);
  760. if (ProcessInformationList == NULL) {
  761. Status = -1;
  762. goto MainEnd;
  763. }
  764. }
  765. assert(ProcessInformationList != NULL);
  766. //
  767. // Display the column headers and print the data for each process.
  768. //
  769. PspPrintHeaders(&Context, Options);
  770. for (Index = 0; Index < ProcessCount; Index += 1) {
  771. if (ProcessInformationList[Index] != NULL) {
  772. PspPrintProcessInformation(&Context,
  773. Options,
  774. ProcessInformationList[Index]);
  775. }
  776. }
  777. Status = 0;
  778. MainEnd:
  779. if ((ProcessIdList != NULL) && (ProcessIdList != Context.ProcessIdList)) {
  780. free(ProcessIdList);
  781. }
  782. if (ProcessInformationList != NULL) {
  783. PspDestroyProcessInformationList(ProcessInformationList, ProcessCount);
  784. }
  785. PspDestroyContext(&Context);
  786. return Status;
  787. }
  788. //
  789. // --------------------------------------------------------- Internal Functions
  790. //
  791. VOID
  792. PspInitializeContext (
  793. PPS_CONTEXT Context
  794. )
  795. /*++
  796. Routine Description:
  797. This routine intitializes the given process status context.
  798. Arguments:
  799. Context - Supplies a pointer to the process status context to initialize.
  800. Return Value:
  801. None.
  802. --*/
  803. {
  804. memset(Context, 0, sizeof(PS_CONTEXT));
  805. INITIALIZE_LIST_HEAD(&(Context->CustomFormatList));
  806. INITIALIZE_LIST_HEAD(&(Context->SessionLeaderList));
  807. INITIALIZE_LIST_HEAD(&(Context->RealGroupIdList));
  808. INITIALIZE_LIST_HEAD(&(Context->TerminalList));
  809. INITIALIZE_LIST_HEAD(&(Context->UserIdList));
  810. INITIALIZE_LIST_HEAD(&(Context->RealUserIdList));
  811. return;
  812. }
  813. VOID
  814. PspDestroyContext (
  815. PPS_CONTEXT Context
  816. )
  817. /*++
  818. Routine Description:
  819. This routine destroys the given process status context, without releasing
  820. the pointer itself.
  821. Arguments:
  822. Context - Supplies a pointer to the process status context to destroy.
  823. Return Value:
  824. None.
  825. --*/
  826. {
  827. PPS_FILTER_ENTRY FilterEntry;
  828. PPS_CUSTOM_FORMAT_ENTRY FormatEntry;
  829. if (Context->ProcessIdList != NULL) {
  830. free(Context->ProcessIdList);
  831. }
  832. while (LIST_EMPTY(&(Context->CustomFormatList)) == FALSE) {
  833. FormatEntry = LIST_VALUE(Context->CustomFormatList.Next,
  834. PS_CUSTOM_FORMAT_ENTRY,
  835. ListEntry);
  836. LIST_REMOVE(&(FormatEntry->ListEntry));
  837. free(FormatEntry);
  838. }
  839. while (LIST_EMPTY(&(Context->SessionLeaderList)) == FALSE) {
  840. FilterEntry = LIST_VALUE(Context->SessionLeaderList.Next,
  841. PS_FILTER_ENTRY,
  842. ListEntry);
  843. LIST_REMOVE(&(FilterEntry->ListEntry));
  844. free(FilterEntry);
  845. }
  846. while (LIST_EMPTY(&(Context->RealGroupIdList)) == FALSE) {
  847. FilterEntry = LIST_VALUE(Context->RealGroupIdList.Next,
  848. PS_FILTER_ENTRY,
  849. ListEntry);
  850. LIST_REMOVE(&(FilterEntry->ListEntry));
  851. free(FilterEntry);
  852. }
  853. while (LIST_EMPTY(&(Context->TerminalList)) == FALSE) {
  854. FilterEntry = LIST_VALUE(Context->TerminalList.Next,
  855. PS_FILTER_ENTRY,
  856. ListEntry);
  857. LIST_REMOVE(&(FilterEntry->ListEntry));
  858. free(FilterEntry);
  859. }
  860. while (LIST_EMPTY(&(Context->UserIdList)) == FALSE) {
  861. FilterEntry = LIST_VALUE(Context->UserIdList.Next,
  862. PS_FILTER_ENTRY,
  863. ListEntry);
  864. LIST_REMOVE(&(FilterEntry->ListEntry));
  865. free(FilterEntry);
  866. }
  867. while (LIST_EMPTY(&(Context->RealUserIdList)) == FALSE) {
  868. FilterEntry = LIST_VALUE(Context->RealUserIdList.Next,
  869. PS_FILTER_ENTRY,
  870. ListEntry);
  871. LIST_REMOVE(&(FilterEntry->ListEntry));
  872. free(FilterEntry);
  873. }
  874. return;
  875. }
  876. VOID
  877. PspPrintHeaders (
  878. PPS_CONTEXT Context,
  879. ULONG Options
  880. )
  881. /*++
  882. Routine Description:
  883. This routine displays the gathered process information to standard out.
  884. Arguments:
  885. Context - Supplies a pointer to the application context.
  886. Options - Supplies a bitmask of application options.
  887. Return Value:
  888. None.
  889. --*/
  890. {
  891. PPS_COLUMN Column;
  892. ULONG ColumnCount;
  893. PPS_CUSTOM_FORMAT_ENTRY ColumnEntry;
  894. ULONG ColumnWidth;
  895. PLIST_ENTRY CurrentEntry;
  896. PSTR Header;
  897. UINTN Index;
  898. PPS_DEFAULT_REPORT Report;
  899. PS_DATA_TYPE Type;
  900. //
  901. // Print out the column headers.
  902. //
  903. ColumnCount = 0;
  904. if ((Options & PS_OPTION_CUSTOM_FORMAT) != 0) {
  905. if (Context->DisplayHeaderLine != FALSE) {
  906. CurrentEntry = Context->CustomFormatList.Next;
  907. while (CurrentEntry != &(Context->CustomFormatList)) {
  908. ColumnEntry = LIST_VALUE(CurrentEntry,
  909. PS_CUSTOM_FORMAT_ENTRY,
  910. ListEntry);
  911. CurrentEntry = CurrentEntry->Next;
  912. Column = &(PsColumnInformation[ColumnEntry->Type]);
  913. if (ColumnEntry->HeaderOverrideValid != FALSE) {
  914. Header = ColumnEntry->HeaderOverride;
  915. ColumnWidth = ColumnEntry->HeaderOverrideWidth;
  916. } else {
  917. Header = Column->Header;
  918. ColumnWidth = Column->Width;
  919. }
  920. if (ColumnCount != 0) {
  921. printf(" ");
  922. }
  923. if (Column->RightJustified != FALSE) {
  924. printf("%*s", ColumnWidth, Header);
  925. } else {
  926. printf("%-*s", ColumnWidth, Header);
  927. }
  928. ColumnCount += 1;
  929. }
  930. printf("\r\n");
  931. }
  932. } else {
  933. for (Index = 0; Index < PS_DEFAULT_REPORT_COUNT; Index += 1) {
  934. Report = &(PsDefaultReports[Index]);
  935. Type = PsDataTypeInvalid;
  936. if ((Options & PS_OPTION_REPORT_MASK) == 0) {
  937. if ((Report->Flags & PS_DEFAULT_REPORT_FLAG_BASIC) != 0) {
  938. Type = Report->Type;
  939. }
  940. } else {
  941. if (((Options & PS_OPTION_LONG_REPORT) != 0) &&
  942. ((Report->Flags & PS_DEFAULT_REPORT_FLAG_LONG) != 0)) {
  943. Type = Report->Type;
  944. }
  945. //
  946. // The full report type trumps the long report type.
  947. //
  948. if (((Options & PS_OPTION_FULL_REPORT) != 0) &&
  949. ((Report->Flags & PS_DEFAULT_REPORT_FLAG_FULL) != 0)) {
  950. assert(Report->FullType != PsDataTypeInvalid);
  951. Type = Report->FullType;
  952. }
  953. }
  954. //
  955. // If the report doesn't get included based on the options, skip
  956. // it.
  957. //
  958. if (Type == PsDataTypeInvalid) {
  959. continue;
  960. }
  961. if (ColumnCount != 0) {
  962. printf(" ");
  963. }
  964. Column = &(PsColumnInformation[Type]);
  965. if (Column->RightJustified != FALSE) {
  966. printf("%*s", Column->Width, Column->Header);
  967. } else {
  968. printf("%-*s", Column->Width, Column->Header);
  969. }
  970. ColumnCount += 1;
  971. }
  972. printf("\r\n");
  973. }
  974. return;
  975. }
  976. VOID
  977. PspPrintProcessInformation (
  978. PPS_CONTEXT Context,
  979. ULONG Options,
  980. PSWISS_PROCESS_INFORMATION Information
  981. )
  982. /*++
  983. Routine Description:
  984. This routine displays the gathered process information to standard out.
  985. Arguments:
  986. Context - Supplies a pointer to the application context.
  987. Options - Supplies a bitmask of application options.
  988. Information - Supplies a pointer to the swiss process information to print.
  989. Return Value:
  990. None.
  991. --*/
  992. {
  993. ULONG ColumnCount;
  994. PPS_CUSTOM_FORMAT_ENTRY ColumnEntry;
  995. PLIST_ENTRY CurrentEntry;
  996. UINTN Index;
  997. PPS_DEFAULT_REPORT Report;
  998. PS_DATA_TYPE Type;
  999. //
  1000. // Print out the data for each column.
  1001. //
  1002. ColumnCount = 0;
  1003. if ((Options & PS_OPTION_CUSTOM_FORMAT) != 0) {
  1004. CurrentEntry = Context->CustomFormatList.Next;
  1005. while (CurrentEntry != &(Context->CustomFormatList)) {
  1006. ColumnEntry = LIST_VALUE(CurrentEntry,
  1007. PS_CUSTOM_FORMAT_ENTRY,
  1008. ListEntry);
  1009. CurrentEntry = CurrentEntry->Next;
  1010. if (ColumnCount != 0) {
  1011. printf(" ");
  1012. }
  1013. PspPrintDataType(ColumnEntry->Type, Information);
  1014. ColumnCount += 1;
  1015. }
  1016. } else {
  1017. for (Index = 0; Index < PS_DEFAULT_REPORT_COUNT; Index += 1) {
  1018. Report = &(PsDefaultReports[Index]);
  1019. Type = PsDataTypeInvalid;
  1020. if ((Options & PS_OPTION_REPORT_MASK) == 0) {
  1021. if ((Report->Flags & PS_DEFAULT_REPORT_FLAG_BASIC) != 0) {
  1022. Type = Report->Type;
  1023. }
  1024. } else {
  1025. if (((Options & PS_OPTION_LONG_REPORT) != 0) &&
  1026. ((Report->Flags & PS_DEFAULT_REPORT_FLAG_LONG) != 0)) {
  1027. Type = Report->Type;
  1028. }
  1029. //
  1030. // The full report type trumps the long report type.
  1031. //
  1032. if (((Options & PS_OPTION_FULL_REPORT) != 0) &&
  1033. ((Report->Flags & PS_DEFAULT_REPORT_FLAG_FULL) != 0)) {
  1034. assert(Report->FullType != PsDataTypeInvalid);
  1035. Type = Report->FullType;
  1036. }
  1037. }
  1038. //
  1039. // If the report doesn't get included based on the options, skip
  1040. // it.
  1041. //
  1042. if (Type == PsDataTypeInvalid) {
  1043. continue;
  1044. }
  1045. if (ColumnCount != 0) {
  1046. printf(" ");
  1047. }
  1048. PspPrintDataType(Type, Information);
  1049. ColumnCount += 1;
  1050. }
  1051. }
  1052. printf("\r\n");
  1053. return;
  1054. }
  1055. VOID
  1056. PspPrintDataType (
  1057. PS_DATA_TYPE Type,
  1058. PSWISS_PROCESS_INFORMATION Information
  1059. )
  1060. /*++
  1061. Routine Description:
  1062. This routine prints out the data for the given type based on the supplied
  1063. process information.
  1064. Arguments:
  1065. Type - Supplies the type of data to print out.
  1066. Information - Supplies the process information to print.
  1067. Return Value:
  1068. None.
  1069. --*/
  1070. {
  1071. BOOL AllocatedString;
  1072. PSTR Arguments;
  1073. int BytesConverted;
  1074. size_t CharactersPrinted;
  1075. PPS_COLUMN Column;
  1076. time_t CpuTime;
  1077. struct tm CurrentDate;
  1078. time_t CurrentTime;
  1079. BOOL DataAvailable;
  1080. struct tm DateData;
  1081. ULONG Days;
  1082. time_t ElapsedTime;
  1083. double FloatData;
  1084. ULONG Hours;
  1085. INT IntegerData;
  1086. size_t LengthRemaining;
  1087. ULONG Minutes;
  1088. size_t PageSize;
  1089. INT Result;
  1090. ULONG Seconds;
  1091. size_t SizeData;
  1092. PSTR StringData;
  1093. size_t StringDataSize;
  1094. time_t TimeData;
  1095. size_t WidthRemaining;
  1096. AllocatedString = FALSE;
  1097. Column = &(PsColumnInformation[Type]);
  1098. DataAvailable = TRUE;
  1099. FloatData = 0;
  1100. IntegerData = 0;
  1101. SizeData = 0;
  1102. StringData = NULL;
  1103. //
  1104. // Collect the data for the given type.
  1105. //
  1106. switch (Type) {
  1107. case PsDataAddress:
  1108. //
  1109. // The address is never displayed.
  1110. //
  1111. DataAvailable = FALSE;
  1112. break;
  1113. case PsDataWaitEvent:
  1114. //
  1115. // TODO: Display the name of the routine the process is waiting on.
  1116. //
  1117. DataAvailable = FALSE;
  1118. break;
  1119. case PsDataFlags:
  1120. IntegerData = Information->Flags;
  1121. break;
  1122. case PsDataState:
  1123. StringData = PsProcessStateStrings[Information->State];
  1124. break;
  1125. case PsDataBlockSize:
  1126. PageSize = SwGetPageSize();
  1127. if (PageSize <= 0) {
  1128. DataAvailable = FALSE;
  1129. break;
  1130. }
  1131. SizeData = Information->ImageSize / PageSize;
  1132. break;
  1133. case PsDataVirtualSize:
  1134. SizeData = Information->ImageSize / _1KB;
  1135. break;
  1136. case PsDataCpuPercentage:
  1137. CpuTime = Information->KernelTime + Information->UserTime;
  1138. CurrentTime = time(NULL);
  1139. ElapsedTime = CurrentTime - Information->StartTime;
  1140. if (ElapsedTime != 0) {
  1141. FloatData = (double)CpuTime / (double)ElapsedTime;
  1142. }
  1143. break;
  1144. case PsDataSchedulingTime:
  1145. CpuTime = Information->KernelTime + Information->UserTime;
  1146. CurrentTime = time(NULL);
  1147. ElapsedTime = CurrentTime - Information->StartTime;
  1148. if (ElapsedTime != 0) {
  1149. IntegerData = CpuTime / ElapsedTime;
  1150. }
  1151. break;
  1152. case PsDataPriority:
  1153. IntegerData = Information->Priority;
  1154. break;
  1155. case PsDataNiceValue:
  1156. IntegerData = Information->NiceValue;
  1157. break;
  1158. case PsDataUserIdentifier:
  1159. IntegerData = Information->EffectiveUserId;
  1160. break;
  1161. case PsDataUserIdentifierText:
  1162. case PsDataEffectiveUserIdentifier:
  1163. Result = SwGetUserNameFromId(Information->EffectiveUserId, &StringData);
  1164. if (Result != 0) {
  1165. DataAvailable = FALSE;
  1166. break;
  1167. }
  1168. AllocatedString = TRUE;
  1169. break;
  1170. case PsDataRealUserIdentifier:
  1171. Result = SwGetUserNameFromId(Information->RealUserId, &StringData);
  1172. if (Result != 0) {
  1173. DataAvailable = FALSE;
  1174. break;
  1175. }
  1176. AllocatedString = TRUE;
  1177. break;
  1178. case PsDataRealGroupIdentifier:
  1179. Result = SwGetGroupNameFromId(Information->RealGroupId, &StringData);
  1180. if (Result != 0) {
  1181. DataAvailable = FALSE;
  1182. break;
  1183. }
  1184. AllocatedString = TRUE;
  1185. break;
  1186. case PsDataEffectiveGroupIdentifier:
  1187. Result = SwGetGroupNameFromId(Information->EffectiveGroupId,
  1188. &StringData);
  1189. if (Result != 0) {
  1190. DataAvailable = FALSE;
  1191. break;
  1192. }
  1193. AllocatedString = TRUE;
  1194. break;
  1195. case PsDataTerminal:
  1196. Result = SwGetTerminalNameFromId(Information->TerminalId, &StringData);
  1197. if (Result != 0) {
  1198. DataAvailable = FALSE;
  1199. break;
  1200. }
  1201. AllocatedString = TRUE;
  1202. break;
  1203. case PsDataProcessIdentifier:
  1204. IntegerData = Information->ProcessId;
  1205. break;
  1206. case PsDataParentProcessIdentifier:
  1207. IntegerData = Information->ParentProcessId;
  1208. break;
  1209. case PsDataProcessGroupIdentifier:
  1210. IntegerData = Information->ProcessGroupId;
  1211. break;
  1212. case PsDataStartTime:
  1213. TimeData = Information->StartTime;
  1214. Result = SwBreakDownTime(1, &TimeData, &DateData);
  1215. if (Result != 0) {
  1216. DataAvailable = FALSE;
  1217. break;
  1218. }
  1219. CurrentTime = time(NULL);
  1220. Result = SwBreakDownTime(1, &CurrentTime, &CurrentDate);
  1221. if (Result != 0) {
  1222. DataAvailable = FALSE;
  1223. break;
  1224. }
  1225. break;
  1226. case PsDataElapsedTime:
  1227. CurrentTime = time(NULL);
  1228. TimeData = CurrentTime - Information->StartTime;
  1229. Days = TimeData / PS_SECONDS_PER_DAY;
  1230. TimeData -= Days * PS_SECONDS_PER_DAY;
  1231. Hours = TimeData / PS_SECONDS_PER_HOUR;
  1232. TimeData -= Hours * PS_SECONDS_PER_HOUR;
  1233. Minutes = TimeData / PS_SECONDS_PER_MINUTE;
  1234. Seconds = TimeData - (Minutes * PS_SECONDS_PER_MINUTE);
  1235. assert((Seconds >= 0) && (Seconds < PS_SECONDS_PER_MINUTE));
  1236. if (Days != 0) {
  1237. StringDataSize = strlen(PS_ELAPSED_TIME_DAYS_FORMAT) + 1;
  1238. StringData = malloc(StringDataSize);
  1239. } else if (Hours != 0) {
  1240. StringDataSize = strlen(PS_ELAPSED_TIME_HOURS_FORMAT) + 1;
  1241. StringData = malloc(StringDataSize);
  1242. } else {
  1243. StringDataSize = strlen(PS_ELAPSED_TIME_DEFAULT_FORMAT) + 1;
  1244. StringData = malloc(StringDataSize);
  1245. }
  1246. if (StringData == NULL) {
  1247. DataAvailable = FALSE;
  1248. break;
  1249. }
  1250. AllocatedString = TRUE;
  1251. if (Days != 0) {
  1252. Result = snprintf(StringData,
  1253. StringDataSize,
  1254. PS_ELAPSED_TIME_DAYS_FORMAT,
  1255. Days,
  1256. Hours,
  1257. Minutes,
  1258. Seconds);
  1259. } else if (Hours != 0) {
  1260. Result = snprintf(StringData,
  1261. StringDataSize,
  1262. PS_ELAPSED_TIME_HOURS_FORMAT,
  1263. Hours,
  1264. Minutes,
  1265. Seconds);
  1266. } else {
  1267. Result = snprintf(StringData,
  1268. StringDataSize,
  1269. PS_ELAPSED_TIME_DEFAULT_FORMAT,
  1270. Minutes,
  1271. Seconds);
  1272. }
  1273. if (Result < 0) {
  1274. free(StringData);
  1275. DataAvailable = FALSE;
  1276. AllocatedString = FALSE;
  1277. }
  1278. break;
  1279. case PsDataCpuTime:
  1280. TimeData = Information->KernelTime + Information->UserTime;
  1281. Days = TimeData / PS_SECONDS_PER_DAY;
  1282. TimeData -= Days * PS_SECONDS_PER_DAY;
  1283. Hours = TimeData / PS_SECONDS_PER_HOUR;
  1284. TimeData -= Hours * PS_SECONDS_PER_HOUR;
  1285. Minutes = TimeData / PS_SECONDS_PER_MINUTE;
  1286. Seconds = TimeData - (Minutes * PS_SECONDS_PER_MINUTE);
  1287. assert((Seconds >= 0) && (Seconds < PS_SECONDS_PER_MINUTE));
  1288. if (Days != 0) {
  1289. StringDataSize = strlen(PS_CPU_TIME_DAYS_FORMAT) + 1;
  1290. StringData = malloc(StringDataSize);
  1291. } else {
  1292. StringDataSize = strlen(PS_CPU_TIME_DEFAULT_FORMAT) + 1;
  1293. StringData = malloc(StringDataSize);
  1294. }
  1295. if (StringData == NULL) {
  1296. DataAvailable = FALSE;
  1297. break;
  1298. }
  1299. AllocatedString = TRUE;
  1300. if (Days != 0) {
  1301. Result = snprintf(StringData,
  1302. StringDataSize,
  1303. PS_CPU_TIME_DAYS_FORMAT,
  1304. Days,
  1305. Hours,
  1306. Minutes,
  1307. Seconds);
  1308. } else {
  1309. Result = snprintf(StringData,
  1310. StringDataSize,
  1311. PS_CPU_TIME_DEFAULT_FORMAT,
  1312. Hours,
  1313. Minutes,
  1314. Seconds);
  1315. }
  1316. if (Result < 0) {
  1317. free(StringData);
  1318. DataAvailable = FALSE;
  1319. AllocatedString = FALSE;
  1320. }
  1321. break;
  1322. case PsDataCmdName:
  1323. case PsDataCommandName:
  1324. if (Information->NameLength == 0) {
  1325. DataAvailable = FALSE;
  1326. break;
  1327. }
  1328. StringData = Information->Name;
  1329. break;
  1330. case PsDataCmdArguments:
  1331. case PsDataCommandArguments:
  1332. if (Information->ArgumentsSize != 0) {
  1333. StringData = malloc(Column->Width * sizeof(char));
  1334. if (StringData == NULL) {
  1335. DataAvailable = FALSE;
  1336. break;
  1337. }
  1338. AllocatedString = TRUE;
  1339. Arguments = Information->Arguments;
  1340. LengthRemaining = Information->ArgumentsSize;
  1341. WidthRemaining = Column->Width;
  1342. CharactersPrinted = 0;
  1343. while ((LengthRemaining != 0) && (WidthRemaining != 0)) {
  1344. BytesConverted = snprintf(&(StringData[CharactersPrinted]),
  1345. WidthRemaining,
  1346. "%s ",
  1347. Arguments);
  1348. if (BytesConverted < 0) {
  1349. break;
  1350. }
  1351. CharactersPrinted += BytesConverted;
  1352. if (BytesConverted > LengthRemaining) {
  1353. LengthRemaining = 0;
  1354. } else {
  1355. LengthRemaining -= BytesConverted;
  1356. }
  1357. if (BytesConverted > WidthRemaining) {
  1358. WidthRemaining = 0;
  1359. } else {
  1360. WidthRemaining -= BytesConverted;
  1361. }
  1362. Arguments += BytesConverted;
  1363. }
  1364. //
  1365. // Add square brackets to the name to signify that the arguments are
  1366. // not available.
  1367. //
  1368. } else if (Information->NameLength != 0) {
  1369. StringDataSize = Information->NameLength + 2;
  1370. StringData = malloc(StringDataSize);
  1371. if (StringData == NULL) {
  1372. DataAvailable = FALSE;
  1373. break;
  1374. }
  1375. AllocatedString = TRUE;
  1376. Result = snprintf(StringData,
  1377. StringDataSize,
  1378. "[%s]",
  1379. Information->Name);
  1380. if (Result < 0) {
  1381. free(StringData);
  1382. DataAvailable = FALSE;
  1383. AllocatedString = FALSE;
  1384. }
  1385. } else {
  1386. DataAvailable = FALSE;
  1387. }
  1388. break;
  1389. default:
  1390. assert(FALSE);
  1391. break;
  1392. }
  1393. //
  1394. // If no data is available, then just print the missing data character.
  1395. //
  1396. if (DataAvailable == FALSE) {
  1397. if (Column->RightJustified != FALSE) {
  1398. printf("%*c", Column->Width, PS_MISSING_DATA_CHARACTER);
  1399. } else {
  1400. printf("%-*c", Column->Width, PS_MISSING_DATA_CHARACTER);
  1401. }
  1402. return;
  1403. }
  1404. //
  1405. // Print the data based on the type.
  1406. //
  1407. switch (Type) {
  1408. case PsDataFlags:
  1409. case PsDataUserIdentifier:
  1410. case PsDataProcessIdentifier:
  1411. case PsDataParentProcessIdentifier:
  1412. case PsDataProcessGroupIdentifier:
  1413. case PsDataSchedulingTime:
  1414. case PsDataPriority:
  1415. case PsDataNiceValue:
  1416. if (Column->RightJustified != FALSE) {
  1417. printf("%*d", Column->Width, IntegerData);
  1418. } else {
  1419. printf("%-*d", Column->Width, IntegerData);
  1420. }
  1421. break;
  1422. case PsDataAddress:
  1423. case PsDataBlockSize:
  1424. case PsDataVirtualSize:
  1425. if (Column->RightJustified != FALSE) {
  1426. printf("%*lu", Column->Width, SizeData);
  1427. } else {
  1428. printf("%-*lu", Column->Width, SizeData);
  1429. }
  1430. break;
  1431. case PsDataState:
  1432. case PsDataUserIdentifierText:
  1433. case PsDataRealUserIdentifier:
  1434. case PsDataEffectiveUserIdentifier:
  1435. case PsDataRealGroupIdentifier:
  1436. case PsDataEffectiveGroupIdentifier:
  1437. case PsDataWaitEvent:
  1438. case PsDataTerminal:
  1439. case PsDataCmdName:
  1440. case PsDataCmdArguments:
  1441. case PsDataCommandName:
  1442. case PsDataCommandArguments:
  1443. case PsDataElapsedTime:
  1444. case PsDataCpuTime:
  1445. assert(StringData != NULL);
  1446. if (Column->RightJustified != FALSE) {
  1447. printf("%*s", Column->Width, StringData);
  1448. } else {
  1449. printf("%-*s", Column->Width, StringData);
  1450. }
  1451. break;
  1452. case PsDataStartTime:
  1453. assert(Column->RightJustified != FALSE);
  1454. //
  1455. // If the process started today, print the current hour and minute at
  1456. // which it started.
  1457. //
  1458. if ((DateData.tm_year == CurrentDate.tm_year) &&
  1459. (DateData.tm_yday == CurrentDate.tm_yday)) {
  1460. printf("%02d:%02d", DateData.tm_hour, DateData.tm_min);
  1461. //
  1462. // If it started this year, print the month and day it started.
  1463. //
  1464. } else if (DateData.tm_year == CurrentDate.tm_year) {
  1465. printf("%s%02d", PsMonths[DateData.tm_mon], DateData.tm_mday);
  1466. //
  1467. // Otherwise display a full date.
  1468. //
  1469. } else {
  1470. printf("%s %02d, %04d",
  1471. PsMonths[DateData.tm_mon],
  1472. DateData.tm_mday,
  1473. DateData.tm_year + 1900);
  1474. }
  1475. break;
  1476. case PsDataCpuPercentage:
  1477. if (Column->RightJustified != FALSE) {
  1478. printf("%*.2f", Column->Width, FloatData);
  1479. } else {
  1480. printf("%-*.2f", Column->Width, FloatData);
  1481. }
  1482. break;
  1483. default:
  1484. assert(FALSE);
  1485. break;
  1486. }
  1487. if (AllocatedString != FALSE) {
  1488. free(StringData);
  1489. }
  1490. return;
  1491. }
  1492. VOID
  1493. PspFilterProcessInformationList (
  1494. PPS_CONTEXT Context,
  1495. ULONG Options,
  1496. PSWISS_PROCESS_INFORMATION *ProcessInformationList,
  1497. UINTN ProcessCount
  1498. )
  1499. /*++
  1500. Routine Description:
  1501. This routine filters the given process list based on filter options.
  1502. Arguments:
  1503. Context - Supplies a pointer to the application context.
  1504. Options - Supplies a pointer to the filter options.
  1505. ProcessInformationList - Supplies an array of pointers to process
  1506. information structures.
  1507. ProcessCount - Supplies the number of processes in the list.
  1508. Return Value:
  1509. None.
  1510. --*/
  1511. {
  1512. PLIST_ENTRY CurrentEntry;
  1513. INT EffectiveUserId;
  1514. PPS_FILTER_ENTRY FilterEntry;
  1515. PSTR GroupName;
  1516. BOOL IncludeProcess;
  1517. UINTN Index;
  1518. PSWISS_PROCESS_INFORMATION Information;
  1519. UINTN ProcessIdIndex;
  1520. INT Result;
  1521. pid_t SessionId;
  1522. PSTR SessionName;
  1523. INT TerminalId;
  1524. PSTR TerminalName;
  1525. PSTR UserName;
  1526. //
  1527. // If the filter option to use all processes is set, it trumps the rest.
  1528. // Just exit.
  1529. //
  1530. if ((Options & PS_OPTION_REPORT_ALL_PROCESSES) != 0) {
  1531. return;
  1532. }
  1533. TerminalId = SwGetTerminalId();
  1534. EffectiveUserId = SwGetEffectiveUserId();
  1535. //
  1536. // Iterate over each process in the list of process information.
  1537. //
  1538. for (Index = 0; Index < ProcessCount; Index += 1) {
  1539. IncludeProcess = FALSE;
  1540. Information = ProcessInformationList[Index];
  1541. if (Information == NULL) {
  1542. continue;
  1543. }
  1544. //
  1545. // If there are no filter options, then the default behavior is in
  1546. // effect. Gather the processes with the same effective user ID as the
  1547. // current user and the same controlling terminal as the invoker.
  1548. //
  1549. if ((Options & PS_OPTION_FILTER_MASK) == 0) {
  1550. if ((Information->EffectiveUserId == EffectiveUserId) &&
  1551. (Information->TerminalId == TerminalId)) {
  1552. IncludeProcess = TRUE;
  1553. }
  1554. //
  1555. // Otherwise go through all filter options, gathering the inclusive OR
  1556. // of all processes that get caught by the filters.
  1557. //
  1558. } else {
  1559. if ((Options & PS_OPTION_REPORT_ALL_TERMINAL_PROCESSES) != 0) {
  1560. if (Information->TerminalId != INVALID_TERMINAL_ID) {
  1561. IncludeProcess = TRUE;
  1562. }
  1563. }
  1564. if ((IncludeProcess == FALSE) &&
  1565. ((Options & PS_OPTION_REPORT_ALL_PROCESSES_NO_LEADERS) != 0)) {
  1566. SessionId = SwGetSessionId(Information->ProcessId);
  1567. if (Information->ProcessId != SessionId) {
  1568. IncludeProcess = TRUE;
  1569. }
  1570. }
  1571. if ((IncludeProcess == FALSE) &&
  1572. ((Options & PS_OPTION_SESSION_LEADERS_LIST) != 0)) {
  1573. assert(LIST_EMPTY(&(Context->SessionLeaderList)) == FALSE);
  1574. SessionName = NULL;
  1575. SessionId = SwGetSessionId(Information->ProcessId);
  1576. CurrentEntry = Context->SessionLeaderList.Next;
  1577. while (CurrentEntry != &(Context->SessionLeaderList)) {
  1578. FilterEntry = LIST_VALUE(CurrentEntry,
  1579. PS_FILTER_ENTRY,
  1580. ListEntry);
  1581. CurrentEntry = CurrentEntry->Next;
  1582. if (FilterEntry->TextId != NULL) {
  1583. if (SessionName == NULL) {
  1584. Result = SwGetSessionNameFromId(SessionId,
  1585. &SessionName);
  1586. if (Result != 0) {
  1587. continue;
  1588. }
  1589. }
  1590. if (strcmp(FilterEntry->TextId, SessionName) == 0) {
  1591. IncludeProcess = TRUE;
  1592. break;
  1593. }
  1594. continue;
  1595. }
  1596. if (FilterEntry->NumericId == SessionId) {
  1597. IncludeProcess = TRUE;
  1598. break;
  1599. }
  1600. }
  1601. if (SessionName != NULL) {
  1602. free(SessionName);
  1603. SessionName = NULL;
  1604. }
  1605. }
  1606. if ((IncludeProcess == FALSE) &&
  1607. ((Options & PS_OPTION_REAL_GROUP_ID_LIST) != 0)) {
  1608. assert(LIST_EMPTY(&(Context->RealGroupIdList)) == FALSE);
  1609. GroupName = NULL;
  1610. CurrentEntry = Context->RealGroupIdList.Next;
  1611. while (CurrentEntry != &(Context->RealGroupIdList)) {
  1612. FilterEntry = LIST_VALUE(CurrentEntry,
  1613. PS_FILTER_ENTRY,
  1614. ListEntry);
  1615. CurrentEntry = CurrentEntry->Next;
  1616. if (FilterEntry->TextId != NULL) {
  1617. if (GroupName == NULL) {
  1618. Result = SwGetGroupNameFromId(
  1619. Information->RealGroupId,
  1620. &GroupName);
  1621. if (Result != 0) {
  1622. continue;
  1623. }
  1624. }
  1625. if (strcmp(FilterEntry->TextId, GroupName) == 0) {
  1626. IncludeProcess = TRUE;
  1627. break;
  1628. }
  1629. continue;
  1630. }
  1631. if (FilterEntry->NumericId == Information->RealGroupId) {
  1632. IncludeProcess = TRUE;
  1633. break;
  1634. }
  1635. }
  1636. if (GroupName != NULL) {
  1637. free(GroupName);
  1638. GroupName = NULL;
  1639. }
  1640. }
  1641. if ((IncludeProcess == FALSE) &&
  1642. ((Options & PS_OPTION_PROCESS_ID_LIST) != 0)) {
  1643. assert(Context->ProcessIdList != NULL);
  1644. for (ProcessIdIndex = 0;
  1645. ProcessIdIndex < Context->ProcessIdCount;
  1646. ProcessIdIndex += 1) {
  1647. if (Context->ProcessIdList[ProcessIdIndex] ==
  1648. Information->ProcessId) {
  1649. IncludeProcess = TRUE;
  1650. break;
  1651. }
  1652. }
  1653. }
  1654. if ((IncludeProcess == FALSE) &&
  1655. ((Options & PS_OPTION_TERMINAL_LIST) != 0)) {
  1656. assert(LIST_EMPTY(&(Context->TerminalList)) == FALSE);
  1657. TerminalName = NULL;
  1658. CurrentEntry = Context->TerminalList.Next;
  1659. while (CurrentEntry != &(Context->TerminalList)) {
  1660. FilterEntry = LIST_VALUE(CurrentEntry,
  1661. PS_FILTER_ENTRY,
  1662. ListEntry);
  1663. CurrentEntry = CurrentEntry->Next;
  1664. if (FilterEntry->TextId != NULL) {
  1665. if (TerminalName == NULL) {
  1666. Result = SwGetTerminalNameFromId(
  1667. Information->TerminalId,
  1668. &TerminalName);
  1669. if (Result != 0) {
  1670. continue;
  1671. }
  1672. }
  1673. if (strcmp(FilterEntry->TextId, TerminalName) == 0) {
  1674. IncludeProcess = TRUE;
  1675. break;
  1676. }
  1677. continue;
  1678. }
  1679. if (FilterEntry->NumericId == Information->TerminalId) {
  1680. IncludeProcess = TRUE;
  1681. break;
  1682. }
  1683. }
  1684. if (TerminalName != NULL) {
  1685. free(TerminalName);
  1686. TerminalName = NULL;
  1687. }
  1688. }
  1689. if ((IncludeProcess == FALSE) &&
  1690. ((Options & PS_OPTION_USER_LIST) != 0)) {
  1691. assert(LIST_EMPTY(&(Context->UserIdList)) == FALSE);
  1692. UserName = NULL;
  1693. CurrentEntry = Context->UserIdList.Next;
  1694. while (CurrentEntry != &(Context->UserIdList)) {
  1695. FilterEntry = LIST_VALUE(CurrentEntry,
  1696. PS_FILTER_ENTRY,
  1697. ListEntry);
  1698. CurrentEntry = CurrentEntry->Next;
  1699. if (FilterEntry->TextId != NULL) {
  1700. if (UserName == NULL) {
  1701. Result = SwGetUserNameFromId(
  1702. Information->EffectiveUserId,
  1703. &UserName);
  1704. if (Result != 0) {
  1705. continue;
  1706. }
  1707. }
  1708. if (strcmp(FilterEntry->TextId, UserName) == 0) {
  1709. IncludeProcess = TRUE;
  1710. break;
  1711. }
  1712. continue;
  1713. }
  1714. if (FilterEntry->NumericId ==
  1715. Information->EffectiveUserId) {
  1716. IncludeProcess = TRUE;
  1717. break;
  1718. }
  1719. }
  1720. if (UserName != NULL) {
  1721. free(UserName);
  1722. UserName = NULL;
  1723. }
  1724. }
  1725. if ((IncludeProcess == FALSE) &&
  1726. ((Options & PS_OPTION_REAL_USER_LIST) != 0)) {
  1727. assert(LIST_EMPTY(&(Context->RealUserIdList)) == FALSE);
  1728. UserName = NULL;
  1729. CurrentEntry = Context->RealUserIdList.Next;
  1730. while (CurrentEntry != &(Context->RealUserIdList)) {
  1731. FilterEntry = LIST_VALUE(CurrentEntry,
  1732. PS_FILTER_ENTRY,
  1733. ListEntry);
  1734. CurrentEntry = CurrentEntry->Next;
  1735. if (FilterEntry->TextId != NULL) {
  1736. if (UserName == NULL) {
  1737. Result = SwGetUserNameFromId(
  1738. Information->RealUserId,
  1739. &UserName);
  1740. if (Result != 0) {
  1741. continue;
  1742. }
  1743. }
  1744. if (strcmp(FilterEntry->TextId, UserName) == 0) {
  1745. IncludeProcess = TRUE;
  1746. break;
  1747. }
  1748. continue;
  1749. }
  1750. if (FilterEntry->NumericId == Information->RealUserId) {
  1751. IncludeProcess = TRUE;
  1752. break;
  1753. }
  1754. }
  1755. if (UserName != NULL) {
  1756. free(UserName);
  1757. UserName = NULL;
  1758. }
  1759. }
  1760. }
  1761. //
  1762. // If none of the options wanted to include the process, then it's
  1763. // lights out for this one.
  1764. //
  1765. if (IncludeProcess == FALSE) {
  1766. SwDestroyProcessInformation(ProcessInformationList[Index]);
  1767. ProcessInformationList[Index] = NULL;
  1768. }
  1769. }
  1770. return;
  1771. }
  1772. INT
  1773. PspParseFormatList (
  1774. PPS_CONTEXT Context,
  1775. PSTR String
  1776. )
  1777. /*++
  1778. Routine Description:
  1779. This routine reads in a string, splits it on commas, and creates custom
  1780. format entries for it. This routine is destructive on the string arguments.
  1781. Arguments:
  1782. Context - Supplies a pointer to the application context.
  1783. String - Supplies the string to split.
  1784. Return Value:
  1785. 0 on success.
  1786. Non-zero on failure.
  1787. --*/
  1788. {
  1789. size_t AllocationSize;
  1790. PPS_CUSTOM_FORMAT_ENTRY Column;
  1791. PPS_COLUMN ColumnInformation;
  1792. PSTR CurrentFormat;
  1793. PPS_CUSTOM_FORMAT_MAP_ENTRY FormatMap;
  1794. PSTR HeaderOverride;
  1795. BOOL Match;
  1796. PSTR NextFormat;
  1797. size_t OverrideLength;
  1798. INT Status;
  1799. Column = NULL;
  1800. Context->DisplayHeaderLine = FALSE;
  1801. OverrideLength = 0;
  1802. //
  1803. // Loop splitting on commas.
  1804. //
  1805. CurrentFormat = String;
  1806. while (TRUE) {
  1807. NextFormat = CurrentFormat;
  1808. while ((*NextFormat != ',') &&
  1809. (*NextFormat != ' ') &&
  1810. (*NextFormat != '\0')) {
  1811. NextFormat += 1;
  1812. }
  1813. //
  1814. // Terminate the current string at this filter.
  1815. //
  1816. if (*NextFormat != '\0') {
  1817. NextFormat[0] = '\0';
  1818. NextFormat += 1;
  1819. //
  1820. // End immediately if the next character is a space, comma, or the
  1821. // null terminator.
  1822. //
  1823. if ((*NextFormat == ',') ||
  1824. (*NextFormat == ' ') ||
  1825. (*NextFormat == '\0')) {
  1826. SwPrintError(0, NULL, "Invalid format list");
  1827. Status = EINVAL;
  1828. goto ParseFormatListEnd;
  1829. }
  1830. //
  1831. // Or prepare to terminate the loop if this is the end of the argument.
  1832. //
  1833. } else {
  1834. NextFormat = NULL;
  1835. }
  1836. HeaderOverride = strchr(CurrentFormat, '=');
  1837. if (HeaderOverride != NULL) {
  1838. assert((NextFormat == NULL) || (NextFormat > HeaderOverride));
  1839. HeaderOverride[0] = '\0';
  1840. HeaderOverride += 1;
  1841. if (NextFormat != NULL) {
  1842. OverrideLength = (UINTN)NextFormat - (UINTN)HeaderOverride;
  1843. } else {
  1844. OverrideLength = strlen(HeaderOverride);
  1845. }
  1846. }
  1847. //
  1848. // Search for the column entry that matches the supplied column.
  1849. //
  1850. Match = FALSE;
  1851. FormatMap = &(PsCustomFormatMap[0]);
  1852. while (FormatMap->Format != NULL) {
  1853. if (strcmp(FormatMap->Format, CurrentFormat) == 0) {
  1854. Match = TRUE;
  1855. break;
  1856. }
  1857. FormatMap += 1;
  1858. }
  1859. if (Match == FALSE) {
  1860. SwPrintError(EINVAL, NULL, "Unknown format '%s'", CurrentFormat);
  1861. Status = EINVAL;
  1862. goto ParseFormatListEnd;
  1863. }
  1864. //
  1865. // Allocate the column list entry and insert it into the list. Make a
  1866. // copy of the header override if it exists.
  1867. //
  1868. AllocationSize = sizeof(PS_CUSTOM_FORMAT_ENTRY);
  1869. if (HeaderOverride != NULL) {
  1870. AllocationSize += OverrideLength + 1;
  1871. }
  1872. Column = malloc(AllocationSize);
  1873. if (Column == NULL) {
  1874. Status = ENOMEM;
  1875. goto ParseFormatListEnd;
  1876. }
  1877. memset(Column, 0, sizeof(PS_CUSTOM_FORMAT_ENTRY));
  1878. Column->Type = FormatMap->Type;
  1879. if (HeaderOverride != NULL) {
  1880. Column->HeaderOverrideValid = TRUE;
  1881. Column->HeaderOverride = (PSTR)(Column + 1);
  1882. strncpy(Column->HeaderOverride,
  1883. HeaderOverride,
  1884. OverrideLength + 1);
  1885. assert(Column->HeaderOverride[OverrideLength] == '\0');
  1886. if (OverrideLength != 0) {
  1887. Context->DisplayHeaderLine = TRUE;
  1888. }
  1889. ColumnInformation = &(PsColumnInformation[Column->Type]);
  1890. if (ColumnInformation->Width < OverrideLength) {
  1891. Column->HeaderOverrideWidth = OverrideLength;
  1892. } else {
  1893. Column->HeaderOverrideWidth = ColumnInformation->Width;
  1894. }
  1895. } else {
  1896. Context->DisplayHeaderLine = TRUE;
  1897. }
  1898. INSERT_BEFORE(&(Column->ListEntry), &(Context->CustomFormatList));
  1899. Column = NULL;
  1900. if (NextFormat == NULL) {
  1901. break;
  1902. }
  1903. CurrentFormat = NextFormat;
  1904. }
  1905. Status = 0;
  1906. ParseFormatListEnd:
  1907. if (Column != NULL) {
  1908. free(Column);
  1909. }
  1910. return Status;
  1911. }
  1912. INT
  1913. PspParseFilterList (
  1914. PLIST_ENTRY ListHead,
  1915. PSTR String
  1916. )
  1917. /*++
  1918. Routine Description:
  1919. This routine reads in a string, splits it on commas, and creates a filter
  1920. entry for each element. This routine is destructive on the string arguments.
  1921. Arguments:
  1922. ListHead - Supplies a pointer to the head of the filter list.
  1923. String - Supplies the string to split.
  1924. Return Value:
  1925. 0 on success.
  1926. Non-zero on failure.
  1927. --*/
  1928. {
  1929. PSTR AfterScan;
  1930. size_t AllocationSize;
  1931. PSTR CurrentFilter;
  1932. PPS_FILTER_ENTRY FilterEntry;
  1933. PSTR NextFilter;
  1934. LONG NumericId;
  1935. INT Status;
  1936. //
  1937. // Iterate over the string to find the filter entries.
  1938. //
  1939. CurrentFilter = String;
  1940. while (TRUE) {
  1941. NextFilter = CurrentFilter;
  1942. while ((*NextFilter != ',') &&
  1943. (*NextFilter != ' ') &&
  1944. (*NextFilter != '\0')) {
  1945. NextFilter += 1;
  1946. }
  1947. //
  1948. // Terminate the current string at this filter.
  1949. //
  1950. if (*NextFilter != '\0') {
  1951. NextFilter[0] = '\0';
  1952. NextFilter += 1;
  1953. //
  1954. // End immediately if the next character is a space, comma, or the
  1955. // null terminator.
  1956. //
  1957. if ((*NextFilter == ',') ||
  1958. (*NextFilter == ' ') ||
  1959. (*NextFilter == '\0')) {
  1960. SwPrintError(0, NULL, "Invalid filter list");
  1961. Status = EINVAL;
  1962. goto ParseFilterListEnd;
  1963. }
  1964. //
  1965. // Or prepare to terminate the loop if this is the end of the argument.
  1966. //
  1967. } else {
  1968. NextFilter = NULL;
  1969. }
  1970. //
  1971. // Attempt to convert the string value to a numeric filter ID.
  1972. //
  1973. NumericId = strtol(CurrentFilter, &AfterScan, 10);
  1974. if (NumericId < 0) {
  1975. SwPrintError(EINVAL,
  1976. NULL,
  1977. "Invalid process filter '%s'",
  1978. CurrentFilter);
  1979. Status = EINVAL;
  1980. goto ParseFilterListEnd;
  1981. }
  1982. //
  1983. // If it wasn't a numer, assume it was a string and get on with
  1984. // allocating a filter entry.
  1985. //
  1986. AllocationSize = sizeof(PS_FILTER_ENTRY);
  1987. if ((AfterScan == CurrentFilter) || (*AfterScan != '\0')) {
  1988. AllocationSize += strlen(CurrentFilter) + 1;
  1989. }
  1990. FilterEntry = malloc(AllocationSize);
  1991. if (FilterEntry == NULL) {
  1992. Status = ENOMEM;
  1993. goto ParseFilterListEnd;
  1994. }
  1995. memset(FilterEntry, 0, AllocationSize);
  1996. if ((AfterScan == CurrentFilter) || (*AfterScan != '\0')) {
  1997. FilterEntry->TextId = (PSTR)(FilterEntry + 1);
  1998. strcpy(FilterEntry->TextId, CurrentFilter);
  1999. } else {
  2000. FilterEntry->NumericId = NumericId;
  2001. }
  2002. INSERT_BEFORE(&(FilterEntry->ListEntry), ListHead);
  2003. FilterEntry = NULL;
  2004. if (NextFilter == NULL) {
  2005. break;
  2006. }
  2007. CurrentFilter = NextFilter;
  2008. }
  2009. Status = 0;
  2010. ParseFilterListEnd:
  2011. return Status;
  2012. }
  2013. INT
  2014. PspParseProcessList (
  2015. PPS_CONTEXT Context,
  2016. PSTR String
  2017. )
  2018. /*++
  2019. Routine Description:
  2020. This routine reads in a string, splits it on commas, and creates a process
  2021. ID array based on it. This routine is destructive on the string arguments.
  2022. Arguments:
  2023. Context - Supplies a pointer to the application context.
  2024. String - Supplies the string to split.
  2025. Return Value:
  2026. 0 on success.
  2027. Non-zero on failure.
  2028. --*/
  2029. {
  2030. PSTR AfterScan;
  2031. UINTN AllocationSize;
  2032. UINTN Index;
  2033. PSTR NextProcessIdString;
  2034. pid_t ProcessId;
  2035. UINTN ProcessIdCount;
  2036. pid_t *ProcessIdList;
  2037. PSTR ProcessIdString;
  2038. INT Status;
  2039. //
  2040. // Run through the string once to determine how many process IDs are there.
  2041. //
  2042. ProcessIdString = String;
  2043. ProcessIdCount = 0;
  2044. do {
  2045. NextProcessIdString = ProcessIdString;
  2046. while ((*NextProcessIdString != ',') &&
  2047. (*NextProcessIdString != ' ') &&
  2048. (*NextProcessIdString != '\0')) {
  2049. NextProcessIdString += 1;
  2050. }
  2051. //
  2052. // If the null terminator was reached, prepare to exit the loop.
  2053. //
  2054. if (*NextProcessIdString == '\0') {
  2055. ProcessIdString = NULL;
  2056. //
  2057. // Otherwise check to make sure the list is valid.
  2058. //
  2059. } else {
  2060. NextProcessIdString += 1;
  2061. //
  2062. // End immediately if the next character is a space, comma, or the
  2063. // null terminator.
  2064. //
  2065. if ((*NextProcessIdString == ',') ||
  2066. (*NextProcessIdString == ' ') ||
  2067. (*NextProcessIdString == '\0')) {
  2068. SwPrintError(0, NULL, "Invalid process ID list");
  2069. Status = EINVAL;
  2070. goto ParseProcessListEnd;
  2071. }
  2072. ProcessIdString = NextProcessIdString;
  2073. }
  2074. ProcessIdCount += 1;
  2075. } while (ProcessIdString != NULL);
  2076. //
  2077. // Allocate an array to store all the process IDs. Add it to the existing
  2078. // list if there is one.
  2079. //
  2080. AllocationSize = ProcessIdCount * sizeof(pid_t);
  2081. AllocationSize += Context->ProcessIdCount * sizeof(pid_t);
  2082. ProcessIdList = realloc(Context->ProcessIdList, AllocationSize);
  2083. if (ProcessIdList == NULL) {
  2084. Status = ENOMEM;
  2085. goto ParseProcessListEnd;
  2086. }
  2087. Context->ProcessIdList = ProcessIdList;
  2088. //
  2089. // Loop over the input again, converting the strings to integers.
  2090. //
  2091. Index = Context->ProcessIdCount;
  2092. ProcessIdString = String;
  2093. while (TRUE) {
  2094. NextProcessIdString = ProcessIdString;
  2095. while ((*NextProcessIdString != ',') &&
  2096. (*NextProcessIdString != ' ') &&
  2097. (*NextProcessIdString != '\0')) {
  2098. NextProcessIdString += 1;
  2099. }
  2100. //
  2101. // Terminate the current string at this filter.
  2102. //
  2103. if (*NextProcessIdString != '\0') {
  2104. NextProcessIdString[0] = '\0';
  2105. NextProcessIdString += 1;
  2106. assert((*NextProcessIdString != ',') &&
  2107. (*NextProcessIdString != ' ') &&
  2108. (*NextProcessIdString != '\0'));
  2109. //
  2110. // Or prepare to terminate the loop if this is the end of the argument.
  2111. //
  2112. } else {
  2113. NextProcessIdString = NULL;
  2114. }
  2115. ProcessId = strtol(ProcessIdString, &AfterScan, 10);
  2116. if ((AfterScan == ProcessIdString) || (*AfterScan != '\0')) {
  2117. SwPrintError(0,
  2118. NULL,
  2119. "Invalid process ID '%s'",
  2120. ProcessIdString);
  2121. Status = EINVAL;
  2122. goto ParseProcessListEnd;
  2123. } else if (ProcessId < 0) {
  2124. SwPrintError(ERANGE, NULL, "Process %d not in range", ProcessId);
  2125. Status = ERANGE;
  2126. goto ParseProcessListEnd;
  2127. }
  2128. Context->ProcessIdList[Index] = ProcessId;
  2129. Index += 1;
  2130. if (NextProcessIdString == NULL) {
  2131. break;
  2132. }
  2133. ProcessIdString = NextProcessIdString;
  2134. }
  2135. assert((Context->ProcessIdCount + ProcessIdCount) == Index);
  2136. Context->ProcessIdCount = Index;
  2137. Status = 0;
  2138. ParseProcessListEnd:
  2139. return Status;
  2140. }
  2141. PSWISS_PROCESS_INFORMATION *
  2142. PspGetProcessInformationList (
  2143. pid_t *ProcessIdList,
  2144. UINTN ProcessCount
  2145. )
  2146. /*++
  2147. Routine Description:
  2148. This routine creates and fills in an array of pointers to process
  2149. information structures. The system will be queried for the process
  2150. information of each of the processes supplied in the process ID list.
  2151. Arguments:
  2152. ProcessIdList - Supplies an array of process IDs.
  2153. ProcessCount - Supplies the length of the array.
  2154. Return Value:
  2155. None.
  2156. --*/
  2157. {
  2158. size_t AllocationSize;
  2159. UINTN Index;
  2160. PSWISS_PROCESS_INFORMATION *ProcessInformationList;
  2161. INT Status;
  2162. //
  2163. // Create an array to store pointers to each process's data.
  2164. //
  2165. AllocationSize = sizeof(PSWISS_PROCESS_INFORMATION) * ProcessCount;
  2166. ProcessInformationList = malloc(AllocationSize);
  2167. if (ProcessInformationList == NULL) {
  2168. Status = -1;
  2169. SwPrintError(ENOMEM, NULL, "Failed to get process status");
  2170. goto GetProcessInformationListEnd;
  2171. }
  2172. memset(ProcessInformationList, 0, AllocationSize);
  2173. //
  2174. // For the remaining process IDs, collect the information needed to display
  2175. // to standard out.
  2176. //
  2177. for (Index = 0; Index < ProcessCount; Index += 1) {
  2178. Status = SwGetProcessInformation(ProcessIdList[Index],
  2179. &(ProcessInformationList[Index]));
  2180. if (Status != 0) {
  2181. continue;
  2182. }
  2183. }
  2184. Status = 0;
  2185. GetProcessInformationListEnd:
  2186. if (Status != 0) {
  2187. if (ProcessInformationList != NULL) {
  2188. PspDestroyProcessInformationList(ProcessInformationList,
  2189. ProcessCount);
  2190. ProcessInformationList = NULL;
  2191. }
  2192. }
  2193. return ProcessInformationList;
  2194. }
  2195. VOID
  2196. PspDestroyProcessInformationList (
  2197. PSWISS_PROCESS_INFORMATION *ProcessInformationList,
  2198. UINTN ProcessCount
  2199. )
  2200. /*++
  2201. Routine Description:
  2202. This routine destroys a list of process information.
  2203. Arguments:
  2204. ProcessInformationList - Supplies an array of pointers to process
  2205. information structures.
  2206. ProcessCount - Supplies the length of the array.
  2207. Return Value:
  2208. None.
  2209. --*/
  2210. {
  2211. UINTN Index;
  2212. for (Index = 0; Index < ProcessCount; Index += 1) {
  2213. if (ProcessInformationList[Index] != NULL) {
  2214. SwDestroyProcessInformation(ProcessInformationList[Index]);
  2215. }
  2216. }
  2217. free(ProcessInformationList);
  2218. return;
  2219. }
  2220. VOID
  2221. PspRemoveDuplicateProcessIds (
  2222. pid_t *ProcessIdList,
  2223. PUINTN ProcessIdCount
  2224. )
  2225. /*++
  2226. Routine Description:
  2227. This routine removes the duplicates from a list of process IDs. As a side
  2228. effect, the list is also sorted.
  2229. Arguments:
  2230. ProcessIdList - Supplies an array of product ID from which duplicates will
  2231. be removed.
  2232. ProcessIdCount - Supplies a pointer that on input contains the number of
  2233. process IDs in the list. And on output, receives the updated number of
  2234. process IDs in the list.
  2235. Return Value:
  2236. None.
  2237. --*/
  2238. {
  2239. UINTN Count;
  2240. UINTN Index;
  2241. assert(ProcessIdCount != 0);
  2242. //
  2243. // First sort the list to make removing duplicates easier.
  2244. //
  2245. qsort(ProcessIdList, *ProcessIdCount, sizeof(pid_t), PspCompareProcessIds);
  2246. //
  2247. // Go through list removing the duplicates.
  2248. //
  2249. Count = 1;
  2250. for (Index = 1; Index < *ProcessIdCount; Index += 1) {
  2251. if (ProcessIdList[Count - 1] != ProcessIdList[Index]) {
  2252. ProcessIdList[Count] = ProcessIdList[Index];
  2253. Count += 1;
  2254. }
  2255. }
  2256. *ProcessIdCount = Count;
  2257. return;
  2258. }
  2259. INT
  2260. PspCompareProcessIds (
  2261. const VOID *First,
  2262. const VOID *Second
  2263. )
  2264. /*++
  2265. Routine Description:
  2266. This routine compares two process IDs.
  2267. Arguments:
  2268. First - Supplies a pointer to the first process ID.
  2269. Second - Supplies a pointer to the second process ID.
  2270. Return Value:
  2271. 1 if First > Second.
  2272. 0 if First == Second.
  2273. -1 if First < Second.
  2274. --*/
  2275. {
  2276. pid_t *FirstProcessId;
  2277. INT Result;
  2278. pid_t *SecondProcessId;
  2279. FirstProcessId = (pid_t *)First;
  2280. SecondProcessId = (pid_t *)Second;
  2281. if (*FirstProcessId > *SecondProcessId) {
  2282. Result = 1;
  2283. } else if (*FirstProcessId == *SecondProcessId) {
  2284. Result = 0;
  2285. } else {
  2286. Result = -1;
  2287. }
  2288. return Result;
  2289. }