CommandLine.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. /*
  2. htop - CommandLine.c
  3. (C) 2004-2011 Hisham H. Muhammad
  4. (C) 2020-2021 htop dev team
  5. Released under the GNU GPLv2+, see the COPYING file
  6. in the source distribution for its full text.
  7. */
  8. #include "config.h" // IWYU pragma: keep
  9. #include "CommandLine.h"
  10. #include <assert.h>
  11. #include <ctype.h>
  12. #include <getopt.h>
  13. #include <locale.h>
  14. #include <stdbool.h>
  15. #include <stdio.h>
  16. #include <stdlib.h>
  17. #include <string.h>
  18. #include <time.h>
  19. #include <unistd.h>
  20. #include "Action.h"
  21. #include "CRT.h"
  22. #include "DynamicColumn.h"
  23. #include "DynamicMeter.h"
  24. #include "DynamicScreen.h"
  25. #include "Hashtable.h"
  26. #include "Header.h"
  27. #include "IncSet.h"
  28. #include "Machine.h"
  29. #include "MainPanel.h"
  30. #include "MetersPanel.h"
  31. #include "Panel.h"
  32. #include "Platform.h"
  33. #include "Process.h"
  34. #include "ProcessTable.h"
  35. #include "ScreenManager.h"
  36. #include "Settings.h"
  37. #include "Table.h"
  38. #include "UsersTable.h"
  39. #include "XUtils.h"
  40. static void printVersionFlag(const char* name) {
  41. printf("%s " VERSION "\n", name);
  42. }
  43. static void printHelpFlag(const char* name) {
  44. printf("%s " VERSION "\n"
  45. COPYRIGHT "\n"
  46. "Released under the GNU GPLv2+.\n\n"
  47. "-C --no-color Use a monochrome color scheme\n"
  48. "-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
  49. "-F --filter=FILTER Show only the commands matching the given filter\n"
  50. "-h --help Print this help screen\n"
  51. "-H --highlight-changes[=DELAY] Highlight new and old processes\n", name);
  52. #ifdef HAVE_GETMOUSE
  53. printf("-M --no-mouse Disable the mouse\n");
  54. #endif
  55. printf("-n --max-iterations=NUMBER Exit htop after NUMBER iterations/frame updates\n"
  56. "-p --pid=PID[,PID,PID...] Show only the given PIDs\n"
  57. " --readonly Disable all system and process changing features\n"
  58. "-s --sort-key=COLUMN Sort by COLUMN in list view (try --sort-key=help for a list)\n"
  59. "-t --tree Show the tree view (can be combined with -s)\n"
  60. "-u --user[=USERNAME] Show only processes for a given user (or $USER)\n"
  61. "-U --no-unicode Do not use unicode but plain ASCII\n"
  62. "-V --version Print version info\n");
  63. Platform_longOptionsUsage(name);
  64. printf("\n"
  65. "Press F1 inside %s for online help.\n"
  66. "See 'man %s' for more information.\n", name, name);
  67. }
  68. // ----------------------------------------
  69. typedef struct CommandLineSettings_ {
  70. Hashtable* pidMatchList;
  71. char* commFilter;
  72. uid_t userId;
  73. int sortKey;
  74. int delay;
  75. int iterationsRemaining;
  76. bool useColors;
  77. #ifdef HAVE_GETMOUSE
  78. bool enableMouse;
  79. #endif
  80. bool treeView;
  81. bool allowUnicode;
  82. bool highlightChanges;
  83. int highlightDelaySecs;
  84. bool readonly;
  85. } CommandLineSettings;
  86. static CommandLineStatus parseArguments(int argc, char** argv, CommandLineSettings* flags) {
  87. *flags = (CommandLineSettings) {
  88. .pidMatchList = NULL,
  89. .commFilter = NULL,
  90. .userId = (uid_t)-1, // -1 is guaranteed to be an invalid uid_t (see setreuid(2))
  91. .sortKey = 0,
  92. .delay = -1,
  93. .iterationsRemaining = -1,
  94. .useColors = true,
  95. #ifdef HAVE_GETMOUSE
  96. .enableMouse = true,
  97. #endif
  98. .treeView = false,
  99. .allowUnicode = true,
  100. .highlightChanges = false,
  101. .highlightDelaySecs = -1,
  102. .readonly = false,
  103. };
  104. const struct option long_opts[] =
  105. {
  106. {"help", no_argument, 0, 'h'},
  107. {"version", no_argument, 0, 'V'},
  108. {"delay", required_argument, 0, 'd'},
  109. {"max-iterations", required_argument, 0, 'n'},
  110. {"sort-key", required_argument, 0, 's'},
  111. {"user", optional_argument, 0, 'u'},
  112. {"no-color", no_argument, 0, 'C'},
  113. {"no-colour", no_argument, 0, 'C'},
  114. {"no-mouse", no_argument, 0, 'M'},
  115. {"no-unicode", no_argument, 0, 'U'},
  116. {"tree", no_argument, 0, 't'},
  117. {"pid", required_argument, 0, 'p'},
  118. {"filter", required_argument, 0, 'F'},
  119. {"highlight-changes", optional_argument, 0, 'H'},
  120. {"readonly", no_argument, 0, 128},
  121. PLATFORM_LONG_OPTIONS
  122. {0, 0, 0, 0}
  123. };
  124. int opt, opti = 0;
  125. /* Parse arguments */
  126. while ((opt = getopt_long(argc, argv, "hVMCs:td:n:u::Up:F:H::", long_opts, &opti))) {
  127. if (opt == EOF)
  128. break;
  129. switch (opt) {
  130. case 'h':
  131. printHelpFlag(program);
  132. return STATUS_OK_EXIT;
  133. case 'V':
  134. printVersionFlag(program);
  135. return STATUS_OK_EXIT;
  136. case 's':
  137. assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
  138. if (String_eq(optarg, "help")) {
  139. for (int j = 1; j < LAST_PROCESSFIELD; j++) {
  140. const char* name = Process_fields[j].name;
  141. const char* description = Process_fields[j].description;
  142. if (name)
  143. printf("%19s %s\n", name, description);
  144. }
  145. return STATUS_OK_EXIT;
  146. }
  147. flags->sortKey = 0;
  148. for (int j = 1; j < LAST_PROCESSFIELD; j++) {
  149. if (Process_fields[j].name == NULL)
  150. continue;
  151. if (String_eq(optarg, Process_fields[j].name)) {
  152. flags->sortKey = j;
  153. break;
  154. }
  155. }
  156. if (flags->sortKey == 0) {
  157. fprintf(stderr, "Error: invalid column \"%s\".\n", optarg);
  158. return STATUS_ERROR_EXIT;
  159. }
  160. break;
  161. case 'd':
  162. if (sscanf(optarg, "%16d", &(flags->delay)) == 1) {
  163. if (flags->delay < 1)
  164. flags->delay = 1;
  165. if (flags->delay > 100)
  166. flags->delay = 100;
  167. } else {
  168. fprintf(stderr, "Error: invalid delay value \"%s\".\n", optarg);
  169. return STATUS_ERROR_EXIT;
  170. }
  171. break;
  172. case 'n':
  173. if (sscanf(optarg, "%16d", &flags->iterationsRemaining) == 1) {
  174. if (flags->iterationsRemaining <= 0) {
  175. fprintf(stderr, "Error: maximum iteration count must be positive.\n");
  176. return STATUS_ERROR_EXIT;
  177. }
  178. } else {
  179. fprintf(stderr, "Error: invalid maximum iteration count \"%s\".\n", optarg);
  180. return STATUS_ERROR_EXIT;
  181. }
  182. break;
  183. case 'u': {
  184. const char* username = optarg;
  185. if (!username && optind < argc && argv[optind] != NULL &&
  186. (argv[optind][0] != '\0' && argv[optind][0] != '-')) {
  187. username = argv[optind++];
  188. }
  189. if (!username) {
  190. flags->userId = geteuid();
  191. } else if (!Action_setUserOnly(username, &(flags->userId))) {
  192. for (const char* itr = username; *itr; ++itr)
  193. if (!isdigit((unsigned char)*itr)) {
  194. fprintf(stderr, "Error: invalid user \"%s\".\n", username);
  195. return STATUS_ERROR_EXIT;
  196. }
  197. flags->userId = atol(username);
  198. }
  199. break;
  200. }
  201. case 'C':
  202. flags->useColors = false;
  203. break;
  204. case 'M':
  205. #ifdef HAVE_GETMOUSE
  206. flags->enableMouse = false;
  207. #endif
  208. break;
  209. case 'U':
  210. flags->allowUnicode = false;
  211. break;
  212. case 't':
  213. flags->treeView = true;
  214. break;
  215. case 'p': {
  216. assert(optarg); /* please clang analyzer, cause optarg can be NULL in the 'u' case */
  217. char* argCopy = xStrdup(optarg);
  218. char* saveptr;
  219. const char* pid = strtok_r(argCopy, ",", &saveptr);
  220. if (!flags->pidMatchList) {
  221. flags->pidMatchList = Hashtable_new(8, false);
  222. }
  223. while (pid) {
  224. unsigned int num_pid = atoi(pid);
  225. // deepcode ignore CastIntegerToAddress: we just want a non-NULL pointer here
  226. Hashtable_put(flags->pidMatchList, num_pid, (void*) 1);
  227. pid = strtok_r(NULL, ",", &saveptr);
  228. }
  229. free(argCopy);
  230. break;
  231. }
  232. case 'F':
  233. assert(optarg);
  234. free_and_xStrdup(&flags->commFilter, optarg);
  235. break;
  236. case 'H': {
  237. const char* delay = optarg;
  238. if (!delay && optind < argc && argv[optind] != NULL &&
  239. (argv[optind][0] != '\0' && argv[optind][0] != '-')) {
  240. delay = argv[optind++];
  241. }
  242. if (delay) {
  243. if (sscanf(delay, "%16d", &(flags->highlightDelaySecs)) == 1) {
  244. if (flags->highlightDelaySecs < 1)
  245. flags->highlightDelaySecs = 1;
  246. } else {
  247. fprintf(stderr, "Error: invalid highlight delay value \"%s\".\n", delay);
  248. return STATUS_ERROR_EXIT;
  249. }
  250. }
  251. flags->highlightChanges = true;
  252. break;
  253. }
  254. case 128:
  255. flags->readonly = true;
  256. break;
  257. default: {
  258. CommandLineStatus status;
  259. if ((status = Platform_getLongOption(opt, argc, argv)) != STATUS_OK)
  260. return status;
  261. break;
  262. }
  263. }
  264. }
  265. if (optind < argc) {
  266. fprintf(stderr, "Error: unsupported non-option ARGV-elements:");
  267. while (optind < argc)
  268. fprintf(stderr, " %s", argv[optind++]);
  269. fprintf(stderr, "\n");
  270. return STATUS_ERROR_EXIT;
  271. }
  272. return STATUS_OK;
  273. }
  274. static void CommandLine_delay(Machine* host, unsigned long millisec) {
  275. struct timespec req = {
  276. .tv_sec = 0,
  277. .tv_nsec = millisec * 1000000L
  278. };
  279. while (nanosleep(&req, &req) == -1)
  280. continue;
  281. Platform_gettime_realtime(&host->realtime, &host->realtimeMs);
  282. }
  283. static void setCommFilter(State* state, char** commFilter) {
  284. Table* table = state->host->activeTable;
  285. IncSet* inc = state->mainPanel->inc;
  286. IncSet_setFilter(inc, *commFilter);
  287. table->incFilter = IncSet_filter(inc);
  288. free(*commFilter);
  289. *commFilter = NULL;
  290. }
  291. int CommandLine_run(int argc, char** argv) {
  292. /* initialize locale */
  293. const char* lc_ctype;
  294. if ((lc_ctype = getenv("LC_CTYPE")) || (lc_ctype = getenv("LC_ALL")))
  295. setlocale(LC_CTYPE, lc_ctype);
  296. else
  297. setlocale(LC_CTYPE, "");
  298. CommandLineStatus status = STATUS_OK;
  299. CommandLineSettings flags = { 0 };
  300. if ((status = parseArguments(argc, argv, &flags)) != STATUS_OK)
  301. return status != STATUS_OK_EXIT ? 1 : 0;
  302. if (flags.readonly)
  303. Settings_enableReadonly();
  304. if (!Platform_init())
  305. return 1;
  306. UsersTable* ut = UsersTable_new();
  307. Hashtable* dm = DynamicMeters_new();
  308. Hashtable* dc = DynamicColumns_new();
  309. Hashtable* ds = DynamicScreens_new();
  310. Machine* host = Machine_new(ut, flags.userId);
  311. ProcessTable* pt = ProcessTable_new(host, flags.pidMatchList);
  312. Settings* settings = Settings_new(host, dm, dc, ds);
  313. Machine_populateTablesFromSettings(host, settings, &pt->super);
  314. Header* header = Header_new(host, 2);
  315. Header_populateFromSettings(header);
  316. if (flags.delay != -1)
  317. settings->delay = flags.delay;
  318. if (!flags.useColors)
  319. settings->colorScheme = COLORSCHEME_MONOCHROME;
  320. #ifdef HAVE_GETMOUSE
  321. if (!flags.enableMouse)
  322. settings->enableMouse = false;
  323. #endif
  324. if (flags.treeView)
  325. settings->ss->treeView = true;
  326. if (flags.highlightChanges)
  327. settings->highlightChanges = true;
  328. if (flags.highlightDelaySecs != -1)
  329. settings->highlightDelaySecs = flags.highlightDelaySecs;
  330. if (flags.sortKey > 0) {
  331. // -t -s <key> means "tree sorted by key"
  332. // -s <key> means "list sorted by key" (previous existing behavior)
  333. if (!flags.treeView) {
  334. settings->ss->treeView = false;
  335. }
  336. ScreenSettings_setSortKey(settings->ss, flags.sortKey);
  337. }
  338. host->iterationsRemaining = flags.iterationsRemaining;
  339. CRT_init(settings, flags.allowUnicode, flags.iterationsRemaining != -1);
  340. MainPanel* panel = MainPanel_new();
  341. Machine_setTablesPanel(host, (Panel*) panel);
  342. MainPanel_updateLabels(panel, settings->ss->treeView, flags.commFilter);
  343. State state = {
  344. .host = host,
  345. .mainPanel = panel,
  346. .header = header,
  347. .pauseUpdate = false,
  348. .hideSelection = false,
  349. .hideMeters = false,
  350. };
  351. MainPanel_setState(panel, &state);
  352. if (flags.commFilter)
  353. setCommFilter(&state, &(flags.commFilter));
  354. ScreenManager* scr = ScreenManager_new(header, host, &state, true);
  355. ScreenManager_add(scr, (Panel*) panel, -1);
  356. Machine_scan(host);
  357. Machine_scanTables(host);
  358. CommandLine_delay(host, 75);
  359. Machine_scan(host);
  360. Machine_scanTables(host);
  361. if (settings->ss->allBranchesCollapsed)
  362. Table_collapseAllBranches(&pt->super);
  363. ScreenManager_run(scr, NULL, NULL, NULL);
  364. Platform_done();
  365. CRT_done();
  366. if (settings->changed) {
  367. #ifndef NDEBUG
  368. if (!String_eq(settings->initialFilename, settings->filename))
  369. fprintf(stderr, "Configuration %s was resolved to %s\n", settings->initialFilename, settings->filename);
  370. #endif /* NDEBUG */
  371. int r = Settings_write(settings, false);
  372. if (r < 0)
  373. fprintf(stderr, "Can not save configuration to %s: %s\n", settings->filename, strerror(-r));
  374. }
  375. Header_delete(header);
  376. Machine_delete(host);
  377. ScreenManager_delete(scr);
  378. MetersPanel_cleanup();
  379. UsersTable_delete(ut);
  380. if (flags.pidMatchList)
  381. Hashtable_delete(flags.pidMatchList);
  382. CRT_resetSignalHandlers();
  383. /* Delete these last, since they can get accessed in the crash handler */
  384. Settings_delete(settings);
  385. DynamicColumns_delete(dc);
  386. DynamicMeters_delete(dm);
  387. DynamicScreens_delete(ds);
  388. return 0;
  389. }